浅析linux内核内存管理之最终内核页表
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
浅析linux内核内存管理之最终内核页表
在系统初始化的时候进行了最终内核映射,主要在paging_init函数中:
- 499void __init paging_init(void)
- 500{
- 501#ifdef CONFIG_X86_PAE
- 502 set_nx();
- 503 if (nx_enabled)
- 504 printk("NX (Execute Disable) protection: active\n");
- 505#endif
- 506
- 507 pagetable_init();
- 508
- 509 load_cr3(swapper_pg_dir);
- 510
- 511#ifdef CONFIG_X86_PAE
- 512 /*
- 513 * We will bail out later - printk doesn't work right now so
- 514 * the user would just see a hanging kernel.
- 515 */
- 516 if (cpu_has_pae)
- 517 set_in_cr4(X86_CR4_PAE);
- 518#endif
- 519 __flush_tlb_all();
- 520
- 521 kmap_init();
- 522 zone_sizes_init();
- 523}
- 其中pagetable_init函数主要对直接映射区,永久映射区,固定映射区的页表进行了设置。当设置临时页表的时候,swapper_pg_dir是存放临时页全局目录,范围是0~4G。在建立最终内核页表的时候,仍然使用了swapper_pg_dir这个变量,在pagetable_init函数中对其进行了重新的设置,使其保存3G~4G的页全局目录部分的地址
- 把swapper_pg_dir的物理地址存放在cr3中
- 如果CONFIG_X86_PAE,则设置CR4寄存器的第5位,即PAE标志位
- 使tlb无效
- 临时内核映射占用的空间是固定内核映射的一部分,从FIX_KMAP_BEGIN~FIX_KMAP_END,由于之前已经设置了固定映射的页中间目录,页表等,所以临时内核映射的页表是存在的。在kmap_init函数中主要就是获得临时内核映射区的第一个页表,即FIX_KMAP_BEGIN对应的页表,将地址存放在kmap_pte变量中
下面分析pagetable_init是怎样建立内核地址空间与直接映射区物理内存的映射的:
- 310static void __init pagetable_init (void)
- 311{
- 312 unsigned long vaddr;
- 313 pgd_t *pgd_base = swapper_pg_dir;
- 314
- 315#ifdef CONFIG_X86_PAE
- 316 int i;
- 317 /* Init entries of the first-level page table to the zero page */
- 318 for (i = 0; i < PTRS_PER_PGD; i++)
- 319 set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT));
- 320#endif
- 321
- 322 /* Enable PSE if available */
- 323 if (cpu_has_pse) {
- 324 set_in_cr4(X86_CR4_PSE);
- 325 }
- 326
- 327 /* Enable PGE if available */
- 328 if (cpu_has_pge) {
- 329 set_in_cr4(X86_CR4_PGE);
- 330 __PAGE_KERNEL |= _PAGE_GLOBAL;
- 331 __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;
- 332 }
- 333
- 334 kernel_physical_mapping_init(pgd_base);
- 335 remap_numa_kva();
- 336
- 337 /*
- 338 * Fixed mappings, only the page table structure has to be
- 339 * created - mappings will be set by set_fixmap():
- 340 */
- 341 vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
- 342 page_table_range_init(vaddr, 0, pgd_base);
- 343
- 344 permanent_kmaps_init(pgd_base);
- 345
- 346#ifdef CONFIG_X86_PAE
- 347 /*
- 348 * Add low memory identity-mappings - SMP needs it when
- 349 * starting up on an AP from real-mode. In the non-PAE
- 350 * case we already have these mappings through head.S.
- 351 * All user-space mappings are explicitly cleared after
- 352 * SMP startup.
- 353 */
- 354 pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
- 355#endif
- 356}
- 如果是PAE的,那么使用页目录指针表(PDPT),此时pgd只有4项,将其填充为零页的地址
- 如果设置了PSE,则置位CR4寄存器的第4位
- 如果设置了PGE,则置位CR4寄存器的第7位
- 设置直接映射区的页表结构
- 获得固定映射区起始的线性地址,调用page_table_range_init函数,设置固定映射部分的master kernel page directory,pmd,pte。
- 调用permanent_kmaps_init函数设置永久映射部分。建立从PKMAP_BASE~PKMAP_BASE+PAGE_SIZE*LASTKMAP的映射,永久映射只使用内核中的一个页表,所以在开启PAE的时候为2MB,否则为4MB,并将页表的地址放入pkmap_page_table变量中
直接映射区的页表建立由kernel_physical_mapping_init函数完成:
- 143static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
- 144{
- 145 unsigned long pfn;
- 146 pgd_t *pgd;
- 147 pmd_t *pmd;
- 148 pte_t *pte;
- 149 int pgd_idx, pmd_idx, pte_ofs;
- 150
- 151 pgd_idx = pgd_index(PAGE_OFFSET);
- 152 pgd = pgd_base + pgd_idx;
- 153 pfn = 0;
- 154
- 155 for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
- 156 pmd = one_md_table_init(pgd);
- 157 if (pfn >= max_low_pfn)
- 158 continue;
- 159 for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
- 160 unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
- 161
- 162 /* Map with big pages if possible, otherwise create normal page tables. */
- 163 if (cpu_has_pse) {
- 164 unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;
- 165
- 166 if (is_kernel_text(address) || is_kernel_text(address2))
- 167 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));
- 168 else
- 169 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));
- 170 pfn += PTRS_PER_PTE;
- 171 } else {
- 172 pte = one_page_table_init(pmd);
- 173
- 174 for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) {
- 175 if (is_kernel_text(address))
- 176 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
- 177 else
- 178 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
- 179 }
- 180 }
- 181 }
- 182 }
- 183}
- 通过pgd_index计算PAGE_OFFSET在临时页全局目录中的索引值
- 由于要映射的是直接映射区,所以物理页从物理页0开始
- one_md_table_init函数和one_page_table_init函数会调用alloc_bootmem_low_pages分配pmd,pt占用的page,并填充pgd,pmd相应的相应的表项
- 建立直接映射区物理页与内核线性空间的映射,如果是内核代码段设置为可读,写,执行
注意如果开启了PAE,则设置512项的pmd,否则1项的pmd;当开启PAE,则pte为512项,否则1024项
这样,最终内核页表初始化完成。总结一下:在系统初始化的时候,将直接映射区,永久映射区和固定映射区进行了pgd,pmd,pte的填充。其中直接映射区建立了物理地址到线性地址的映射,而其他的在需要的时候通过kmap,kmap_atomic,set_fixmap进行物理地址到线性地址的映射
再多说一部分,也是系统初始化,页表相关的最后一部分。看一下mem_init函数,除了主要任务bootmem allocator与buddy system的交接之外,还设置了高端内存的页(调用set_highmem_pages_init函数),并调用zap_low_mappings函数清low_memory的映射,内核线程只访问内核空间是不能访问用户空间的,其实low_memory的映射被设置的部分也就是当初为8MB建立的恒等映射填充了临时内核页全局目录的第0项,第1项
- 376void zap_low_mappings (void)
- 377{
- 378 int i;
- 379
- 380 save_pg_dir();
- 381
- 382 /*
- 383 * Zap initial low-memory mappings.
- 384 *
- 385 * Note that "pgd_clear()" doesn't do it for
- 386 * us, because pgd_clear() is a no-op on i386.
- 387 */
- 388 for (i = 0; i < USER_PTRS_PER_PGD; i++)
- 389#ifdef CONFIG_X86_PAE
- 390 set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));
- 391#else
- 392 set_pgd(swapper_pg_dir+i, __pgd(0));
- 393#endif
- 394 flush_tlb_all();
- 395}
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献2条内容
所有评论(0)