分类: LINUX
2013-04-12 10:24:25
linux内核从第二个MB开始,第一个MB通常留给BIOS或者某些设备。下图是Linux2.6的前3MB,而在Linux2.4中只用了2MB,问题是2.6的代码多了。
进程的线性地址空间分为0x00000000-0xbfffffff和0xc0000000-0xffffffff两部分,前面3GB空间是用户地址空间,后面1GB是内核地址空间。那后面的1GB对于所有的进程来说都应该是相同的,它们等于主内核页全局目录(swapper_pg_dir)的表项。
内核的swapper_pg_dir会作为参考模型,为系统中每个普通进程对应的pgdir提供模板。这里有一个如何把对swapper_pg_dir的修改同步到其他进程的pgdir上这个问题,TODO:
内核如何初始化自己的页表
这个过程分为2步:
(1) 内核仅创建足以把自己装入到RAM中的8MB地址空间。目标是允许在实模式和保护模式下都能对0x00000000-0x007fffff进行寻址,也就是把
虚拟地址0x00000000-0x007fffff和0xc0000000-0xc07fffff都映射到物理地址0x00000000-0x007fffff上。这一步是通过startup_32()汇编函数来实现的,startup_32()也开启了分页功能。
(2) 最终内核页表
这里要分三种情况来讨论:
1) 首先是当RAM<896MB时
swapper_pg_dir由paging_init()函数来初始化。假设物理内存有RAMSIZE大小,那该函数就把虚拟地址0xc0000000 -- 0xc0000000+RAMSIZE 映射到物理地址0x00000000开始的RAMSIZE处。然后调用zap_low_mappings来清除在startup_32()映射8MB对应的页表项。
2) 当896MB<=RAM<=4096MB时
前896MB还是直接从0xc0000000开始映射,而超过的部分就使用动态重映射来解决。TODO:动态重映射
3) 当RAM>4096MB时
当RAM大于4GB时,32位的地址就无法对所有的物理内存进行寻址了,这时就需要使用PAE(32位虚拟地址,36位物理地址)。
先回顾一下PAE(Physical Address Extension):
PAE就是一种把32位虚拟地址转换为36位物理地址的一种分页机制。
cr3 –> PDPT(4个64位表项)
虚拟地址位31-30(2bits) –> PDPT中4个项的一个
虚拟地址位29-21(9bits) –> 页目录(512个64位项)中512个项的一个
虚拟地址位20-12(9bits) –> 页表(512个64位项)中512个项的一个
虚拟地址位11-0(12bits) –> 4KB页的偏移量
初始化的时候有以下几个步骤:
a) swapper_pg_dir也就是PDPT的前三项(前3GB的用户地址空间)用empty_zero_page初始化。empty_zero_page就是一个空页。而第四项用中间目录(pmd)的地址来初始化。
b) 页中间目录的前448项(后64项留给非连续内存分配)用RAM前896MB的物理地址填充。
固定映射是一种可以让线性地址和对应的物理地址之间以任意方式来建立的一种映射。它位于线性地址第四个GB的末端。
所有固定映射的线性地址都在fixed_addresses数据结构中由索引表示。
内核提供了函数:
static inline unsigned long fix_to_virt(const unsigned int idx)
来完成从一个index比如FIX_APIC_BASE到其线性地址的映射。内核同样提供了set_fixmap(idx,phys)来设置这个地址映射。
另外固定映射的另一个好处就是它在编译时就可以确定线性地址的值,这样可以提高性能。