页式存储管理并不需要建立在段式存储管理的基础之上,这是两种不同的机制。但80386中,保护模式的实现是与段式存储密不可分的。于是段式管理先将逻辑地址映射成线性地址,然后再由页式管理将线性地址映射成物理地址;或者,当不适用页式管理时,就将线性地址直接用作物理地址。
80386把线性地址空间划分为4K字节的页面,每个页面都可以被映射至物理存储空间中任意一块4k字节大小的空间。在段式管理中,连续的逻辑地址经过地址映射后在线性地址空间还是连续的。但是在页式管理中,连续的线性地址经过映射后在物理空间却不一定连续。
由于页式管理的引入,对32位的线性地址有了新的解释:
typedef struct {
unsigned int dir:10;//用作页面表目录中下标,该目录项指向一个页面表
unsigned int page:10;//用作页面表中的下标,该表项指向一个物理页面
unsigned int offset:12;//4K字节物理页面内的偏移量
}线性地址;
从线性地址到物理地址的映射过程为:
l 从CR3取得页面目录的基地址;(寄存器CR3,当前页面目录的地址)
l 以dir位段为下标,在目录目录中取得相应页表的基地址;
l 以page位段为下标,在所得的页面表中取得相应的页面描述符;
l 将页面描述符中给出的页面基地址与offset位段相加得到物理地址;
一个页面的大小是4K字节,而每一个页面表项或目录表项的大小是4个字节,1024个表项正好也是4K字节,恰好可以放在一个页面中。也正为此,64位的Alpha CPU中页面的大小是8K字节,目录表项和页面表项的大小也变成了8个字节。
目录相中含有一个页面的指针,而页面表中含有一个指向页面的起始的指针。由于页面表和页面的起始地址总是在4K字节的边界上,这些指针的低12位总是0。这样,在目录项和页表项中只要20位用于指针就足够了,余下的12位用于控制。
目录项的结构为:
typedef struct {
unsigned int ptba:20;//页表基地址
unsigned int avail:3; //供系统程序员使用
unsigned int g:1; //global,全局性页面
unsigned int ps:1; //页面大小,0表示4K字节
unsigned int d:1; //reserved,保留,永远为0
unsigned int a:1; //accessed,已被访问过
unsigned int pcd:1; //关闭(不适用)缓冲存储器
unsigned int pwt:1; //用于缓冲存储器
unsigned int u_s:1; //0:系统权限,1:用户权限
unsigned int r_w:1; //只读或可写
unsigned int p:1; //0:不在内存中
} 目录项;
页表项的结构基本与上相同,不同:
l 没有页面大小ps位,保留不用;
l 第7位则为D标志,表示该页面已经被写过;
当页表项或目录项中最低位p为0时:表示相应的页面或者页表不在内存,根据其他一些有关的寄存器的设置,CPU产生一个“页面错”异常(也成为缺页中断)。这样,内核中的有关异常服务程序就可以从磁盘上的页面交换区将相应的页面读入内存,并相应的设置表项中的基地址,并将p置1。相反,也可以将内存中暂不使用的页面写入磁盘的交换区,然后置p为0。这样,就可以实现页式虚存了。当p为0时,表项的其余各位均无意义,也可以被用来临时存放其他信息,如被换出的页面在磁盘上的位置等。
当目录项中的ps为0时:页面表中所有的页面均为4K字节。当ps为1时:页面大小位4M字节,而页面表就不在使用了,这时候线性地址的低22位全部用作4M字节页面中的位移。总的寻址能力并没有改变,但映射的过程减少了一层。访问速度显著提高。
寄存器CR0,其最高位PG是页式映射机制的总开关。