linux 内核源代码情景分析——linux 内存管理的基本框架
阅读原文时间:2023年07月08日阅读:1

386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射。这种映射模式在大多数情况下可以节省页面表所占用的空间。因为大多数进程不会用到整个虚存空间,在虚存空间中通常都留有很大的“空洞”。采用两层的方式,只要一个目录项所对应的的那部分空间是个空洞,就可以把该目录项设置为空,从而剩下了与之对应的页面表。当地址的宽度是32时,两层映射机制比较有效也比较合理,但是,当地址的宽度大于32位时就不够有效了。

因此,linux 内核的映射机制设计成3层,在页面目录和页面表中间增设了一层“中间目录”,在代码中,页面目录称为PGD,中间目录称为PMD,而页面表则称为PT,PT中的表项称为PTE。PGD、PMD和PT都是数组。具体映射如下图:

但是,这个虚拟的映射模型必须落实到具体CPU和MMU的物理映射机制。i386实际上并不是按3层而是按2层来进行地址映射的。从Pentium Pro开始,Intel引入了物理地址扩充功能PAE,允许将地址宽度从32提高到36位,并且在硬件上支持3层映射模型。这样,在Pentium Pro以后的CPU上,只要将CPU的内存管理设置成PAE模式,就能使虚存的映射变为3层模式。

32位地址意味着4G字节的虚存空间,linux内核将这4G字节的空间分成两部分。将最高的1G字节(从虚地址0xC0000000至0xFFFFFFFF),用于内核本身,称为“系统空间”。而将较低的3G自己(从虚地址0x0至0xBFFFFFFF),用作各个进程的“用户空间”,这样,理论上每个进程可以使用的用户空间都是3G字节。虽然各个进程拥有其自己的3G字节用户空间,系统空间却由所有进程共享,每当一个进程通过系统调用进入了内核,该进程就在共享的系统空间中运行,不再有其自己的独立空间。从具体的进程角度看,每个进程都拥有4G字节的虚存空间,较低的3G字节为自己的用户空间,最高的1G字节为所有进程以及内核共享的系统空间,见下图:

虽然系统空间占据了每个虚存空间的最高1G字节,在物理的内存中却总是从最低的地址0开始的,所以,对于内核来说,其地址的映射是很简单的线性映射,0xC0000000就是两者之间的位移量。对于系统空间而言,给定一个虚地址x,其物理地址是从x减去PAGE_OFFSET,相应的,给定一个物理地址,其虚地址是x+PAGE_OFFSET。