linux 内核 内存管理 初始化 页表

linux内核在启动分页机制时就已经有了一个可用的页表,这个最初的页表是手工创建了,而且仅有为数不多的几个页面,进入start_kernel()以后需要把原来的页表完善一下,具体工作在start_kernel() --> setup_arch() --> paging_init()函数中。paing_init()函数定义在arch/x86/mm/init_32.c中。

void __init paging_init(void)
{
#ifdef CONFIG_X86_PAE
    set_nx();
    if (nx_enabled)
        printk("NX (Execute Disable) protection: active\n");
#endif
    pagetable_init();
    load_cr3(swapper_pg_dir);
#ifdef CONFIG_X86_PAE
    if (cpu_has_pae)
        set_in_cr4(X86_CR4_PAE);
#endif
    __flush_tlb_all();
    kmap_init();

}

页表初始化的工作由pagetable_init()函数完成,load_cr3()的功能是装载页表。

static void __init pagetable_init (void)
{
    unsigned long vaddr, end;
    pgd_t *pgd_base = swapper_pg_dir;
    paravirt_pagetable_setup_start(pgd_base); //?什么用?
     /* Enable PSE if available */
    if (cpu_has_pse)
        set_in_cr4(X86_CR4_PSE);
    /* Enable PGE if available */
    if (cpu_has_pge) {
        set_in_cr4(X86_CR4_PGE);
        __PAGE_KERNEL |= _PAGE_GLOBAL;
        __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;
    }
    /*  把0~896M的物理页框映射到3G~3G+896M,在这个过程中会通过
     * bootmem_allocator申请新的内存页,用以存放页表。  */
    kernel_physical_mapping_init(pgd_base);
    remap_numa_kva(); // null function

    /*  Fixed mappings, only the page table structure has to be
     * created - mappings will be set by set_fixmap()   */
    vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
    end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK;
    page_table_range_init(vaddr, end, pgd_base);
    /* 把固定映射内存区映射到页目录中 */
    permanent_kmaps_init(pgd_base);
    paravirt_pagetable_setup_done(pgd_base);

}

kernel_physical_mapping_init()函数把0~896M的物理地址映射到3G以上的虚拟空间。在这个过程中回向bootmem allocator申请需要的内存页(参考bootmem的初始化)。该函数定义在:arch/x86/mm/init_32.c,定义内容如下。

static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
{
    unsigned long pfn;
    pgd_t *pgd;
    pmd_t *pmd;
    pte_t *pte;
    int pgd_idx, pmd_idx, pte_ofs;
    pgd_idx = pgd_index(PAGE_OFFSET);  // get the index of pgd
    pgd = pgd_base + pgd_idx;          // get the pgd entry
    pfn = 0;
    for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
        /* pmd is just pgd in x86 architecture's default config*/
        pmd = one_md_table_init(pgd);
        if (pfn >= max_low_pfn)//这个判断可以保证不建立高端内存对应的页表
            continue;  /* why not break ? 当where pfn >= max_low_pfn时,
                         * 虽然不用建立相应的页表,但是pmd这一层次的目录是需要建立的,
                         * 所以使用continue而不是break */
        /* 下面这个for语句循环的次数取决于 PTRS_PER_PMD宏.
         * PTRS_PER_PMD的定义在include/asm-generic/pgtable-nopmd.h中,被定义为1*/
        for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
            unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
        /* Map with big pages if possible, otherwise create normal page tables. */
            if (cpu_has_pse) {

                unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;

                if (is_kernel_text(address) || is_kernel_text(address2))
                    set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));
                else
                    set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));
                pfn += PTRS_PER_PTE;
            } else {
               /* 如果没有与该pmd entry相对应的page table,
                 * 则为该pmd entry申请一个新的页面作为page table,
                 * one_page_table_init()的返回值即是page table的首地址 */
                pte = one_page_table_init(pmd);
                /* 初始化每个pte的值*/
                for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn;
                     pte++, pfn++, pte_ofs++, address += PAGE_SIZE) {
                    if (is_kernel_text(address))
                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
                    else
                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
                }
            }
        }
    }

}

  函数中使用pmd作为过渡,是为了能够支持3级页表,而linux的x86默认配置是2级页表,所以pmd的存在代码的兼容性。linux在2.6.24中的页表是4级的,从顶到底分别是pgd, pud, pmd和pte,内核在默认配置下屏蔽了pud和pmd。pud的全称是page upper directory, pmd的全程是page middle directory。


你可能感兴趣的:(Linux,Kernel,linux,table,linux内核,x86,structure,工作)