一 静态页表初始化
空间布局
如下图,swapper_pg_dir 代表着一级页表,pg0,pg1,则是内核起始的两个pte页表,包含1~9m 的8m空间,存储内核镜像,
由于vmlinux 的编译基址址是PAGE_OFFSET + 1MiB,所以,这里这三个虚拟地址分别为
swapper_pg_dir:PAGE_OFFSET + 1MiB +0x1000
pg0:PAGE_OFFSET + 1MiB +0x2000
pg1:PAGE_OFFSET + 1MiB +0x3000
线性映射,物理地址 = 虚拟地址-PAGE_OFFSET
这里还有个empty_zero_page 紧随其后,用于存放boot 传递给内核的参数,
arch/i386/kernel/head.S
而内核映像 实际的位置从 text 才开始,即1m+0x5000
swapper_pg_dir页表的初始化
二.内核页表初始化
static void __init pagetable_init (void)
{
unsigned long vaddr, end;
pgd_t *pgd, *pgd_base;
int i, j, k;
pmd_t *pmd;
pte_t *pte, *pte_base;
/*
* This can be zero as well - no problem, in that case we exit
* the loops anyway due to the PTRS_PER_* conditions.
*/
end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); //页表初始化范围,结束地址max_low_pfn,即低端内存结束地址
pgd_base = swapper_pg_dir; //pgd一级页表基址
...
i = __pgd_offset(PAGE_OFFSET);//页表初始化范围,起始地址,--page_offset
pgd = pgd_base + i;
for (; i < PTRS_PER_PGD; pgd++, i++) {
vaddr = i*PGDIR_SIZE;
if (end && (vaddr >= end))
break;
#if CONFIG_X86_PAE
...
#else
pmd = (pmd_t *)pgd;
#endif
if (pmd != pmd_offset(pgd, 0))
BUG();
for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {
vaddr = i*PGDIR_SIZE + j*PMD_SIZE;
if (end && (vaddr >= end))
break;
...
//从页帧分配一页用于pte 页表
pte_base = pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
for (k = 0; k < PTRS_PER_PTE; pte++, k++) {
vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
if (end && (vaddr >= end))
break;
//设置pte页表项
*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
}
//设置pmd即pgd页表项
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base)));
if (pte_base != pte_offset(pmd, 0))
BUG();
}
}
/*
* 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;
fixrange_init(vaddr, 0, pgd_base); //从vaddr 到最大空间结束(0)
#if CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);//为highmmem 初始化页表结构,同样未建立页表映射(pte 页表项未设置)
pgd = swapper_pg_dir + __pgd_offset(vaddr);
pmd = pmd_offset(pgd, vaddr);
pte = pte_offset(pmd, vaddr);
pkmap_page_table = pte;//pte页表基址,最大1024 只需一页
#endif
...
}
fixrange_init
为fixrange 建立 gpd,和pmd页表,注意pte 页表并未建立
```c
static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
int i, j;
unsigned long vaddr;
vaddr = start;//fixrange 的起始线性地址
i = __pgd_offset(vaddr);
j = __pmd_offset(vaddr);
pgd = pgd_base + i;
for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) {
#if CONFIG_X86_PAE
...
#else
pmd = (pmd_t *)pgd;
#endif
for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
if (pmd_none(*pmd)) {
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
if (pte != pte_offset(pmd, 0))
BUG();
}
vaddr += PMD_SIZE;
}
j = 0;
}
}