linux页表机制的建立分为两个阶段,第一个阶段在启动初始化时,调用汇编代码实现临时的分页;第二阶段在内核初始化时建立完整的完整的页表机制。对于物理地址扩展(PAE)分页机制中,intel通过在她得处理器上把管脚数从32增加到36已经满足了这些需求,寻址能力可以达到64GB。不过,只有引入一种新的分页机制把32位线性地址转换为36位物理地址才能使用所增加的物理地址。linux为对多种体系的支持,选择了一套简单的通用实现机制。在这里,只分析x86 32位下的实现。
下图为一个linux分页机制框架图:
一、临时内核页表初始化
在setup_32.S中
page_pde_offset = (__PAGE_OFFSET >> 20);
movl $pa(__brk_base), %edi
movl $pa(swapper_pg_dir), %edx
movl $PTE_IDENT_ATTR, %eax
10:
leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
movl %ecx,(%edx) /* Store identity PDE entry */
movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
addl $4,%edx
movl $1024, %ecx
11:
stosl
addl $0x1000,%eax
loop 11b
/*
* End condition: we must map up to the end + MAPPING_BEYOND_END.
*/
movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
cmpl %ebp,%eax
jb 10b
addl $__PAGE_OFFSET, %edi
movl %edi, pa(_brk_end)
shrl $12, %eax
movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
movl $pa(swapper_pg_fixmap)+PDE_IDENT_ATTR,%eax
movl %eax,pa(swapper_pg_dir+0xffc)
对于页表初始化的原理如果可以参考intel手册。
其中常量PTE_IDENT_ATTR定义在patable_types.h中。
#define PTE_IDENT_ATTR 0x003 /* PRESENT+RW */
二、启动分页机制
实现代码在head_32.S中。
movl $pa(swapper_pg_dir),%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $X86_CR0_PG,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
三、低端内存和高端内存固定映射区页表机制初始化
在start_kernle()->setup_arch()->init_memory_mapping()中实现具体的页表映射机制的建立。
void __init setup_arch(char **cmdline_p)
{
……
/* max_pfn_mapped is updated here */
/*建立分页机制,包括页表的内存分配,映射的建立包括低端内存的映射建立,高端内 存中固定映射区域的映射建立返回的值为低端内存的总共映射的内存大小,页面为单位 */
max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
max_pfn_mapped = max_low_pfn_mapped;//将其保存到变量
……
}
/*
* Setup the direct mapping of the physical memory at PAGE_OFFSET.
* This runs before bootmem is initialized