Linux内核分析(二) 段页式分析
80386CPU 页式存储管理机制
关于80386段式管理,也是Linux内核采用的段式管理部分已在硬件平台分析给出了详细分析(段式存储管理)。 接着我们详细分析建立在段机制上的 80386CPU 页式内存管理机制。分析页机制之前我们先了解为什么我们需要页机制。在之前我们的段式存储管理,解决了安全问题,但是还有很多不能解决问题,这些问题会影响存储,以及指令执行的效率。具体说,80386的段式内存管理机制,是将指令中结合段寄存器使用的32位逻辑地址(转换)成同样式32位的物理地址。但是段式存储管理机制的灵活性和效率都比较差。例如“段”式可变长度的,这给内存和硬盘交换带来了麻烦。而页机制就很好的解决了这些麻烦,页机制中,逻辑地址连续性并不会和实际物理地址连续对应,其次一页的大小固定为4K,细化内存管理的粒度,方便内存的换入换出。在只有段机制下,逻辑地址(虚拟地址)经过段部件产生的线性地址被直接当做物理地址,而有了页机制后,经过段部件的线性地址还要经过页部件的映射为实际的物理地址。如同所示:
如上图,如果我们开启页机制,那么原先选择子和偏移经过段部件形成的32位线性地址不再是物理地址,而是成为页部件的输入。因此段部件输出的32位的线性地址会有新的解释。如下同所示:
数据结构代码表示:
typedef struct{ unsigned int dir:10; //用作页面表目录的下标,该目录项指向一个页表 unsigned int page:10; //用作具体页面表中的下标,该表项指向一个物理页面 unsigned int offset:12; //在4K字节物理页面内的偏移量 }//32位线性地址(段部件的输出,页部件的输入):后面为字段所占位数
其中页目录dir有1024个项,每个项占4字节,因此共4K字节,刚好一个页大小,每个项指向一个页面表。一个页面表含1024个物理页面基址,每项占4字节,因此共4K字节,也刚好一个页大小。页内偏移offset用与一个物理页内的寻址。
类似GDTR和LDTR,又增加了一个新的寄存器CR3寄存器作为指向当前页目录的指针存放的寄存器。具体
线性地址到物理地址的映射过程为:
①从CR3取得页目录的基地址。
②以线性地址中的dir位段为下标,在目录中取得相应页面的基地址。
③以线性地址中的page位段为下标,在所得的页面表中取得相应的页面描述项。
④将页面描述项中给出的页面基地址与线性地址中的offset位相加得到物理地址。
具体图示如下:
我们可以看到,目录项和页表项都是对应一个4K页面,即地址为4K整数倍,因此4字节,32位地址的后12位是空出来的,而实际被用来当作一些属性位。
例如目录项的结构为:
数据结构代码表示:
typedef struct{ unsigned int ptba:20; //页表基址的高20位 unsigned int avail:3; //供系统程序员使用 unsigned int g:1; //global 全局性页面 unsigned int ps:1; //页面大小,0表示4K字节 unsigned int reserved:1; //保留,永远是0 unsigned int a:1; //accessed,已被访问过 unsigned int pcd:1; //关闭(不使用)缓冲存储器 unsigned int pwt:1; //write Through,用于缓冲寄存器 unsigned int u_s:1; //为0表示系统(或超级)权限,为1表示用户权限 unsigned int r_w:1; //只读或可写 unsigned int p:1; //为0时表示相应的页面不存在 }//页目录项
以上为80386的页式存储管理,我们是以二级页表为例子做的分析。而Linux对与页式管理的机制与其类似,只是在具体的实现上不同。