linux 2.6源代码情景分析笔记之内存6

当ram小于896mb时的最终内核页表

由内核页表所提供的最终映射必须把从0xc0000000开始的线性地址转化为从0开始的物理地址。
#define __pa(x)   ((unsigned long)(x)-PAGE_OFFSET)将从page_offset开始的线性地址转换成相应的物理地址。
#define __va(x)   ((void *)((unsigned long)(x)+PAGE_OFFSET))与上面的反向。

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
        /*We will bail out later - printk doesn't work right now so the user would just see a hanging kernel.*/
        if (cpu_has_pae)
                set_in_cr4(X86_CR4_PAE);
#endif
        __flush_tlb_all();
        kmap_init();
        zone_sizes_init();
}

看这个函数中最基本的一些程序行,瘦身后的函数:
几个变量:pgd_t描述页全局目录项,pmd_t描述页中间项,pud_t页上级目录,pte_t页表项。
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))找到线性地址address对应的目录项在页全局目录中的索引(相对位置)
通过宏与pgd_base相加,得到当前pgd。在外层循环中遍历,循环中宏的作用是表示页全局目录表中表项个数。进函数one_md_table_init。
pud = pud_offset(pgd, 0);接收指向页全局目录项的指针pgd和线性地址addr作为参数。这个宏产生页上级目录中目录项addr对应的线性地址。在两级或者三级分页系统中,产生pgd,一个页全局目录项的地址。
pmd_table = pmd_offset(pud, 0);产生了目录项addr在页中间目录中的偏移地址,在两级或者三级分页系统中,产生pud,即页全局目录项的地址。最后返回一个pmd_table.
然后回到kernel函数中,继续遍历,内层循环的作用是遍历页中间目录个数,同时保证没有越界。然后在其中建立pte。
然后将地址映射入虚拟空间。
然后页面表固定地址初始化。

static void __init pagetable_init (void)
{
        unsigned long vaddr;
        pgd_t *pgd_base = swapper_pg_dir;主内核页全局目录。
        kernel_physical_mapping_init(pgd_base);
        remap_numa_kva();
        vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
        page_table_range_init(vaddr, 0, pgd_base);
        permanent_kmaps_init(pgd_base);
}

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);
        pgd = pgd_base + pgd_idx;
        pfn = 0;

        for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
                pmd = one_md_table_init(pgd);
                if (pfn >= max_low_pfn)
                        continue;
                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 {
                                pte = one_page_table_init(pmd);

                                for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) {
                                                if (is_kernel_text(address))
                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
                                                else
                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
                                }
                        }
                }
        }
}

static pmd_t * __init one_md_table_init(pgd_t *pgd)
{
        pud_t *pud;
        pmd_t *pmd_table;

#ifdef CONFIG_X86_PAE
        pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
        set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
        pud = pud_offset(pgd, 0);
        if (pmd_table != pmd_offset(pud, 0))
                BUG();
#else
        pud = pud_offset(pgd, 0);
        pmd_table = pmd_offset(pud, 0);
#endif

        return pmd_table;
}

static pte_t * __init one_page_table_init(pmd_t *pmd)
{
        if (pmd_none(*pmd)) {
                pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
                set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
                if (page_table != pte_offset_kernel(pmd, 0))
                        BUG();

                return page_table;
        }

        return pte_offset_kernel(pmd, 0);
}
page_table_range_init(vaddr, 0, pgd_base);
static void __init page_table_range_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
{
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
        int pgd_idx, pmd_idx;
        unsigned long vaddr;

        vaddr = start;
        pgd_idx = pgd_index(vaddr);
        pmd_idx = pmd_index(vaddr);
        pgd = pgd_base + pgd_idx;

        for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) {
                if (pgd_none(*pgd))
                        one_md_table_init(pgd);
                pud = pud_offset(pgd, vaddr);
                pmd = pmd_offset(pud, vaddr);
                for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); pmd++, pmd_idx++) {
                        if (pmd_none(*pmd))
                                one_page_table_init(pmd);

                        vaddr += PMD_SIZE;
                }
                pmd_idx = 0;
        }
}

void __init permanent_kmaps_init(pgd_t *pgd_base)
{
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *pte;
        unsigned long vaddr;

        vaddr = PKMAP_BASE;
        page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);

        pgd = swapper_pg_dir + pgd_index(vaddr);
        pud = pud_offset(pgd, vaddr);
        pmd = pmd_offset(pud, vaddr);
        pte = pte_offset_kernel(pmd, vaddr);
        pkmap_page_table = pte;
}


void __init zone_sizes_init(void)
{
        unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
        unsigned int max_dma, high, low;

        max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
        low = max_low_pfn;
        high = highend_pfn;

        if (low < max_dma)
                zones_size[ZONE_DMA] = low;
        else {
                zones_size[ZONE_DMA] = max_dma;
                zones_size[ZONE_NORMAL] = low - max_dma;
#ifdef CONFIG_HIGHMEM
                zones_size[ZONE_HIGHMEM] = high - low;
#endif
        }
        free_area_init(zones_size);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值