您的位置:首页 > 运维架构 > Linux

linux内核学习笔记【二】最终内核页表 Final kernel Page Table

2011-11-10 16:02 417 查看
  之前建立了临时页表,现在要建立最终内核页表,内核必须首先要建立一个完整的页表才能继续运行,因为内存寻址是内核继续运行的前提。下面就对主要函数kernel_physical_mapping_init(),进行分析。这个函数的掉用关系为:head.S->start_kernel()->setup_arch()->paging_init()->pagetable_init()->kernel_physical_mapping_init.

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);
//#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) in inculde/pagetable.h
//so pgd_index = (0xc0000000 >> 22) & (1024 - 1) = 0x300 & 0x3ff = 0x300 = 768
pgd = pgd_base + pgd_idx;
//pgd_t *pgd_base = swapper_pg_dir;
//pgd = swapper_pg_dir[0x300]
pfn = 0;

//for(pgd_idx = 768; pgd_idx < 1024; pgd++,pgd_indx++)
for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
pmd = one_md_table_init(pgd);
//pmd = pgd
if (pfn >= max_low_pfn) //max_low_pfn = 1024 * 1024?
continue;
for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
//only have 2 level directory, so PTRS_PER_PMD = 1
unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
//address = 0 * 4096 + 0xc0000000 = 0xc0000000
//            1 * 4096              = 0xc0001000
//                                        ...

/* 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);
//then we have mapped one page table

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));
//set every page table entry PAGE_KERNEL_EXEC or PAGE_KERNEL depend onthe address
}
}
}
}
}

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

#ifdef CONFIG_X86_PAE
//...
#else
pud = pud_offset(pgd, 0);
//#define pud_offset(pgd, start)        (pgd) ;in include/generic/4level-fixup.h
//so pud = pgd;
pmd_table = pmd_offset(pud, 0);
//#define pmd_offset(pud, address) ((pmd_t *) pud_page(*(pud)) + pmd_index(address)) ; in /include/asm-i386/pgtable-3level.h
//so pmd_table = pgd
#endif

return pmd_table;
}

/*
* Create a page table and place a pointer to it in a middle page
* directory entry.
*/
static pte_t * __init one_page_table_init(pmd_t *pmd)
{
if (pmd_none(*pmd)) {
//check if the pmd so as pgd has value
pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
//alloc physical memory, size = 4096
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
//set parameters
if (page_table != pte_offset_kernel(pmd, 0))
BUG();

return page_table;
}

return pte_offset_kernel(pmd, 0);
}

static inline int is_kernel_text(unsigned long addr)
{
if (addr >= (unsigned long)_stext && addr <= (unsigned long)__init_end)
return 1;
return 0;
}

//_stext, __init_end是个内核符号, 在内核链接的时候生成的, 分别表示内核代码段的开始和终止地址.
//如果address属于内核代码段, 那么在设置页表项的时候就要加个PAGE_KERNEL_EXEC属性,如果不是,则加个PAGE_KERNEL属性.
//#define _PAGE_KERNEL_EXEC \
//        (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)

//#define _PAGE_KERNEL \
//        (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_NX)


这样,内核空间的线性地址0xc0000000-0xffffffff已经都映射了整块物理内存。即pgd[0x300]-pgd[0x400]都有了值,同时下面的每个pte[0-1024]也都有了值,不知道理解的对不对,希望高手指点。还有一个疑问就是,用户空间的线性地址0x00000000 - 0xbfffffff这段地址是在什么时候映射的,可能还没有看到吧,继续学习。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: