i386的页式内存管理机制(Linux源代码情景分析读书笔记)

来源:互联网 发布:阿里云 谷歌api 编辑:程序博客网 时间:2024/06/05 14:30

1   内存管理有两种,一种是段式管理,另一种是页式管理,而页式管理更为先进。段式存储管理机制的灵活性和效率都比较差。一方面“段”是可变长度的,这就给盘区交换操作带来了不便;另一方面,如果为了增加灵活性而将一个进程的空间划分成多小段时,就势必要求在程序中频繁地改变段寄存器的内容。同时,如果将段分小,虽然一个段描述表中可以容纳8192个描述项,也未必就能保证足够使用。80386的系统结构决定了它的页式存管智能建立在段式存管的基础上。这也意味着,页式存管的作用是在由段式存管所映射而成的地址上再加上一层地址映射。此时由段式存管映射而成的地址不再是“物理地址”了,Intel就称之为“线性地址”。于是,段式存管先将逻辑地址映射成线性地址,然后再由页式存管将线性地址映射成物理地址;或者,当不适用页式存管时,就将线性地址直接用作物理地址。

2  80386把线性地址空间划分成4K字节的页面,每个页面可以被映射至物理存储空间中任意一块4K字节大小的区间。在段式存管中,连续的逻辑地址经过映射后在线性地址空间还是连续的。但是在页式存管中,连续的线性地址经过映射后在物理空间却不一定连续(其灵活性也在于此)。

由于页式存管的引入,对32位的线性地址有了新的解释(以前就是物理地址):

typedef  struct {

unsigned int dir:10;//用作页面表目录中的下标,该目录项指向一个页面表

unsigned int page:10; //用作具体页面表中的下标,该表项指向一个物理页面

unsigned int offset:12; // 在4K字节物理页面内的偏移量

} 线性地址;

可以看出,在页面目录中共有2^10 = 1024个目录项,每个目录项指向一个页面表,而在每个页面表中又共有1024个页面描述项。类似于GDTR和LDTR,又增加一个新的寄存器CR3作为指向当前页面目录的指针。这样,从线性地址到物理地址的映射过程为:

(1)从CR3取得页面目录的基地址。

(2)以线性地址中的dir位段为下标,在目录中取得相应页面表的基地址。

(3)以线性地址中的page位段为下标,在所得到的页面表中取得相应的页面描述项。

(4)将页面描述项中给出的页面基地址与线性地址中的offset位段相加得到物理地址。

上述过程可以通过下图直观表示:

                               

那么,为什么要使用两个层次,先找到目录项,再找到页面描述项,而不是像在使用段寄存器时那样一步到位呢?

这是出于空间效率的考量。如果将线性地址中的dir和page两个位段合并在一起是20位,因此页面表的大小就是1K X 1K = 1M个表项。由于每个页面的大小为4K字节,总的大小仍为4K  X  1M = 4G,正好是32位地址空间的大小。但是,实际上不会有一个进程会需要用到4G的全部空间,所以大部分表项势必是空着的。可是,在一个数组中,即使是空着不用的表项也占用空间,这样就造成了浪费。而若分成了两层,则页表可以视需要而设置,如果目录中某项为空,就不必设立响应的页表,从而省下了存储空间。

3 目录项中含有指向一个页面表的指针(线性地址中10位的dir项和CR3寄存器共同寻址得出目录项,目录项和线性地址中10位的page项共同寻址得出 此指针),而页面表项中则含有指向一个页面起始地址的指针。由于页面表和页面的起始地址都总是在4K字节的边界上,这些指针的低12位都永远是0.这样,在目录项和页表项中都只要20位用于指针就够了,而余下的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时表示响应的页面不在内存中

}目录项;

页表项的结构基本上与此相同,但没有“页面大小”为ps,所以第8位保留不用,但第7位则为D标志,表示该页面已经被写过,所以已经“脏”了。当目录项或者页表项中的最低位p为0时,表示相应的页面或页面表不在内存,根据其他一些有关寄存器的设置,CPU可以产生一个“页面错Page Fault”异常(也被称为缺页中断,但异常和中断其实是有区别的)。这样,内核中的有关异常服务程序就可以从磁盘上的页面交换区将相应的页面读入内存,并且相应地设置表项中的基地址,并将p位设置成1.相反,也可以将内存中暂不使用的页面写入磁盘交换区,然后将相应页面表项的p位设置成0.这样,就可以实现页式虚存了。当p位为0时,表项的其余各位均无意义,所以可被用来临时存储其他信息,如被换出的页面在磁盘上的位置等等。

当目录项中的ps(page size)位为0时,包含在由该目录项所指向的页面表中所有页面的大小都是4K字节,这也是目前在Linux内核中所采用的页面大小。但是从Pentium处理器开始,Intel引入了PSE页面大小扩充机制。当ps位为1时,页面的大小就成了4M字节,而页面表就不在使用了。这时候,线性地址的低22位就全部用作在4M字节页面中的位移。

4 最后,i386 CPU 中还有个寄存器CR0,其最高位PG是页式映射机制的总开关。当PG位被设置成1时,CPU就开启了页式存储管理的映射机制。


0 0
原创粉丝点击