首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

基于80x86的Linux的分段和分页机制(2)

基于80x86的Linux的分段和分页机制(2)

2.1 页全局目录
页全局目录表,最多可包含1024个页目录项,每个页目录项为4个字节,算起来正好一个页面,结构如图所示:



·第0位是存在位,Present标志:如果被置为1,所指的页(或页表)就在主存中;如果该标志为0,则这一页不在主存中,此时这个表项剩余的位可由操作系统用于自己的目的。如果执行一个地址转换所需的页表项或页目录项中Present标志被清0,那么分页单元就把该线性地址存放在控制寄存器cr2中,并产生14号异常:缺页异常。(我们将在后面的一系列博客中重点讨论Linux如何使用这个字段)。


·第1位是读/写位,第2位是用户/管理员位,Read/Write标志:含有页或页表的存取权限(Read/Write或Read);User/Supervisor标志:含有访问页或页表所需的特权级。这两位为页目录项提供硬件保护。当特权级为3的进程要想访问页面时,需要通过页保护检查,而特权级为0的进程就可以绕过页保护,如图所示:



·第3位是PWT(PageWrite-Through)位,表示是否采用写透方式,写透方式就是既写内存(RAM)也写高速缓存,该位为1表示采用写透方式

·第4位是PCD(Page Cache Disable)位,表示是否启用高速缓存,该位为1表示启用高速缓存。

·第5位是访问位,Accessed标志:当对页目录项进行访问时,A位=1。每当分页单元对相应页框进行寻址时就设置这个标志。当选中的页被交换出去时,这一标志就可以由操作系统使用。分页单元从不重置这个标志;而是必须由操作系统去做。

·第6位Dirty标志,对于页全局目录项,其始终为1。

·第7位是Page Size标志,只适用于页目录项。如果置为1,页目录项指的是4MB的页面,请看后面的扩展分页。


·第8位是Global 标志:只应用于页表项。这个标志是在PentiumPro引入的,用来防止常用页从TLB高速缓存中刷新出去。只有在cr4寄存器的页全局启用(Page GlobalEnable,PGE)标志置位时这个标志才起作用。


·第9~11位由操作系统专用,Linux也没有做特殊之用

2.2 页表80386的每个页目录项指向一个页表,页表最多含有1024个页面项,每项4个字节,包含页面的起始地址和有关该页面的信息。页面的起始地址也是4K的整数倍,所以页面的低12位也留作它用,如图所示。



第31~12位是20位物理页面地址,除第6位外第0~5位及9~11位的用途和页目录项一样,第6位是页面项独有的,当对涉及的页面进行写操作时,D位被置1。

4GB的存储器只有一个页目录,它最多有1024个页目录项,每个页目录项又含有1024个页面项,因此,存储器一共可以分成1024×1024=1M个页面。由于每个页面为4K个字节,所以,存储器的大小正好最多为4GB。
2.3 线性地址到物理地址的转换
当访问一个操作单元时,如何由分段结构确定的32位线性地址通过分页操作转化成32位物理地址呢?过程如图所示。


第一步,CR3包含着页目录的起始地址,用32位线性地址的最高10位A31~A22作为页目录的页目录项的索引,将它乘以4,与CR3中的页目录的起始地址相加,形成相应页表的地址。

第二步,从指定的地址中取出32位页目录项,它的低12位为0,这32位是页表的起始地址。用32位线性地址中的A21~A12位作为页表中的页面的索引,将它乘以4,与页表的起始地址相加,形成32位页面地址。

第三步,将A11~A0作为相对于页面地址的偏移量,与32位页面地址相加,形成32位物理地址。

下面,我们就通过一个实例来介绍一下常规分页是如何工作的。我们假定内核已给一个正在运行的进程分配的线性地址空间范围是0x20000000到0x2003ffff(3GB线性地址空间是一个上限,用户态进程只是引用其中的一个子集)。这个空间正好由64页面组成。其实我们并不必关心包含这些页的页框的物理地址,为什么呢?事实上,其中的一些页甚至可能不在主存中。我们只关注页表项中剩余的字段。

让我们从分配给进程的线性地址的最高10位开始。这两个地址都以2开头后面跟着0,因此高10位有相同的值,即0x080或十进制的128。因此,这两个地址的页目录(Directory字段)都指向进程页目录的第129项。相应的目录项中必须包含分配给该进程的页表的物理地址。如果没有给这个进程分配其它的线性地址,页目录的其余1023项都填为0。


中间10位的值(即Table字段的值)范围从0到0x03f,或十进制的从0到63。因而只有页表的前64个表项是有意义的,其余960表项都填0。

假设进程需要读线性地址0x20021406中的字节。这个地址由分页单元按下面的方法处理:
1. Directory字段的0x80用于选择页目录的第0x80目录项,此目录项指向和该进程的页相关的页表。
2. Table字段0x21用于选择页表的第0x21表项,此表项指向包含所需页的页框。
3. 最后,Offet字段0x406用于在目标页框中读偏移量为0x406中的字节。


如果页表第0x21表项的Present标志为0,则此页就不在主存中;在这种情况下,分页单元在线性地址转换的同时产生一个缺页异常。无论何时,当进程试图访问限定在0x20000000到0x2003ffff范围之外的线性地址时,都将产生一个缺页异常,因为这些页表项都填充了0,尤其是它们的Present标志都被清0。


当今,Linux采用了一种同时适用于32位和64位系统的普通分页模型。前面我们看到,两级页表对32位系统来说已经足够了,但64位系统需要更多数量的分页级别。直到2.6.10版本,Linux采用三级分页的模型。从2.6.11版本开始,采用了四级分页模型:
继承事业,薪火相传
返回列表