浅析linux内核内存管理之最终内核页表

                                  



在系统初始化的时候进行了最终内核映射,主要在paging_init函数中:

[html]  view plain copy
  1. 499void __init paging_init(void)  
  2. 500{  
  3. 501#ifdef CONFIG_X86_PAE  
  4. 502        set_nx();  
  5. 503        if (nx_enabled)  
  6. 504                printk("NX (Execute Disable) protection: active\n");  
  7. 505#endif  
  8. 506  
  9. 507        pagetable_init();  
  10. 508  
  11. 509        load_cr3(swapper_pg_dir);  
  12. 510  
  13. 511#ifdef CONFIG_X86_PAE  
  14. 512        /*  
  15. 513         * We will bail out later - printk doesn't work right now so  
  16. 514         * the user would just see a hanging kernel.  
  17. 515         */  
  18. 516        if (cpu_has_pae)  
  19. 517                set_in_cr4(X86_CR4_PAE);  
  20. 518#endif  
  21. 519        __flush_tlb_all();  
  22. 520  
  23. 521        kmap_init();  
  24. 522        zone_sizes_init();  
  25. 523}  

  1. 其中pagetable_init函数主要对直接映射区,永久映射区,固定映射区的页表进行了设置。当设置临时页表的时候,swapper_pg_dir是存放临时页全局目录,范围是0~4G。在建立最终内核页表的时候,仍然使用了swapper_pg_dir这个变量,在pagetable_init函数中对其进行了重新的设置,使其保存3G~4G的页全局目录部分的地址
  2. 把swapper_pg_dir的物理地址存放在cr3中
  3. 如果CONFIG_X86_PAE,则设置CR4寄存器的第5位,即PAE标志位
  4. 使tlb无效
  5. 临时内核映射占用的空间是固定内核映射的一部分,从FIX_KMAP_BEGIN~FIX_KMAP_END,由于之前已经设置了固定映射的页中间目录,页表等,所以临时内核映射的页表是存在的。在kmap_init函数中主要就是获得临时内核映射区的第一个页表,即FIX_KMAP_BEGIN对应的页表,将地址存放在kmap_pte变量中


下面分析pagetable_init是怎样建立内核地址空间与直接映射区物理内存的映射的:

[html]  view plain copy
  1. 310static void __init pagetable_init (void)  
  2. 311{  
  3. 312        unsigned long vaddr;  
  4. 313        pgd_t *pgd_base = swapper_pg_dir;  
  5. 314  
  6. 315#ifdef CONFIG_X86_PAE  
  7. 316        int i;  
  8. 317        /* Init entries of the first-level page table to the zero page */  
  9. 318        for (i = 0; i < PTRS_PER_PGD; i++)  
  10. 319                set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT));  
  11. 320#endif  
  12. 321  
  13. 322        /* Enable PSE if available */  
  14. 323        if (cpu_has_pse) {  
  15. 324                set_in_cr4(X86_CR4_PSE);  
  16. 325        }  
  17. 326  
  18. 327        /* Enable PGE if available */  
  19. 328        if (cpu_has_pge) {  
  20. 329                set_in_cr4(X86_CR4_PGE);  
  21. 330                __PAGE_KERNEL |= _PAGE_GLOBAL;  
  22. 331                __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;  
  23. 332        }  
  24. 333  
  25. 334        kernel_physical_mapping_init(pgd_base);  
  26. 335        remap_numa_kva();  
  27. 336  
  28. 337        /*  
  29. 338         * Fixed mappings, only the page table structure has to be  
  30. 339         * created - mappings will be set by set_fixmap():  
  31. 340         */  
  32. 341        vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;  
  33. 342        page_table_range_init(vaddr, 0, pgd_base);  
  34. 343  
  35. 344        permanent_kmaps_init(pgd_base);  
  36. 345  
  37. 346#ifdef CONFIG_X86_PAE  
  38. 347        /*  
  39. 348         * Add low memory identity-mappings - SMP needs it when  
  40. 349         * starting up on an AP from real-mode. In the non-PAE  
  41. 350         * case we already have these mappings through head.S.  
  42. 351         * All user-space mappings are explicitly cleared after  
  43. 352         * SMP startup.  
  44. 353         */  
  45. 354        pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];  
  46. 355#endif  
  47. 356}  
  1. 如果是PAE的,那么使用页目录指针表(PDPT),此时pgd只有4项,将其填充为零页的地址
  2. 如果设置了PSE,则置位CR4寄存器的第4位
  3. 如果设置了PGE,则置位CR4寄存器的第7位
  4. 设置直接映射区的页表结构
  5. 获得固定映射区起始的线性地址,调用page_table_range_init函数,设置固定映射部分的master kernel page directory,pmd,pte。
  6. 调用permanent_kmaps_init函数设置永久映射部分。建立从PKMAP_BASE~PKMAP_BASE+PAGE_SIZE*LASTKMAP的映射,永久映射只使用内核中的一个页表,所以在开启PAE的时候为2MB,否则为4MB,并将页表的地址放入pkmap_page_table变量中

直接映射区的页表建立由kernel_physical_mapping_init函数完成:

[html]  view plain copy
  1. 143static void __init kernel_physical_mapping_init(pgd_t *pgd_base)  
  2. 144{  
  3. 145        unsigned long pfn;  
  4. 146        pgd_t *pgd;  
  5. 147        pmd_t *pmd;  
  6. 148        pte_t *pte;  
  7. 149        int pgd_idx, pmd_idx, pte_ofs;  
  8. 150  
  9. 151        pgd_idx = pgd_index(PAGE_OFFSET);  
  10. 152        pgd = pgd_base + pgd_idx;  
  11. 153        pfn = 0;  
  12. 154  
  13. 155        for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {  
  14. 156                pmd = one_md_table_init(pgd);  
  15. 157                if (pfn >= max_low_pfn)  
  16. 158                        continue;  
  17. 159                for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {  
  18. 160                        unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;  
  19. 161  
  20. 162                        /* Map with big pages if possible, otherwise create normal page tables. */  
  21. 163                        if (cpu_has_pse) {  
  22. 164                                unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;  
  23. 165  
  24. 166                                if (is_kernel_text(address) || is_kernel_text(address2))  
  25. 167                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));  
  26. 168                                else  
  27. 169                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));  
  28. 170                                pfn += PTRS_PER_PTE;  
  29. 171                        } else {  
  30. 172                                pte = one_page_table_init(pmd);  
  31. 173  
  32. 174                                for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) {  
  33. 175                                                if (is_kernel_text(address))  
  34. 176                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));  
  35. 177                                                else  
  36. 178                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));  
  37. 179                                }  
  38. 180                        }  
  39. 181                }  
  40. 182        }  
  41. 183}  
  1. 通过pgd_index计算PAGE_OFFSET在临时页全局目录中的索引值
  2. 由于要映射的是直接映射区,所以物理页从物理页0开始
  3. one_md_table_init函数和one_page_table_init函数会调用alloc_bootmem_low_pages分配pmd,pt占用的page,并填充pgd,pmd相应的相应的表项
  4. 建立直接映射区物理页与内核线性空间的映射,如果是内核代码段设置为可读,写,执行
注意如果开启了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项
[html]  view plain copy
  1. 376void zap_low_mappings (void)  
  2. 377{  
  3. 378        int i;  
  4. 379  
  5. 380        save_pg_dir();  
  6. 381  
  7. 382        /*  
  8. 383         * Zap initial low-memory mappings.  
  9. 384         *  
  10. 385         * Note that "pgd_clear()" doesn't do it for  
  11. 386         * us, because pgd_clear() is a no-op on i386.  
  12. 387         */  
  13. 388        for (i = 0; i < USER_PTRS_PER_PGD; i++)  
  14. 389#ifdef CONFIG_X86_PAE  
  15. 390                set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));  
  16. 391#else  
  17. 392                set_pgd(swapper_pg_dir+i, __pgd(0));  
  18. 393#endif  
  19. 394        flush_tlb_all();  
  20. 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 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐