|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
第16章 分页机制和动态页面分配
Intel处理器访问内存的基本策略是分段。在16位实模式下,段的起始位置必须对齐在16字节边界上,而且段的长度最大为64KB。
进入32位保护模式之后,进一步强化了分段功能,并提供了保护机制。此时,段可以起始于任何位置,段的长度可以扩展到处理器的最大寻址范围。典型第,早期的32位处理器拥有32根地址线,因此,段的长度可以扩展到4GB。
在32位保护模式下,对段的访问本着“先登记,后访问”的原则进行。登记就是在GDT或者LDT中登记段的描述符,规定了段的地址和边界,以及访问权限;访问时,则需要拿着一个段描述符的选择子才行,这就是传说中的“虎符”。处理器用段界限和特权级别来审查对段的访问,任何非法的造访行为都会被处理器阻止,并立即拉响警报,也就是所谓的异常中断。
一般来说,人们使用计算机要先安装一个操作系统。在这种情况下,段是由操作系统负责管理的。操作系统加载应用程序,根据程序的要求,为它创建一个或多个段,然后把控制权交给它。
当同时运行的程序和任务很多时,内存可能就不够用了。这时,操作系统的价值就体现出来了。每个段描述符有A位,每当访问一个段时,处理器会将其置位。A位的清零由操作系统定时进行,它可以借此机会统计段的访问频度。当内存不够用时,它可以将那些较少访问的段换到到磁盘上,以腾出空间来给马上要运行的段使用。一旦某个段被挪到磁盘上,操作系统应当将其描述符的P位清零。过一段时间,当这个段又被访问时,因其描述符的P位是“0”,处理器引发不存在异常(中断号为11)。这类中断通常是由操作系统负责处理的,它会用同样的方法腾出空间,将这个段的内容从磁盘调入内存。当这类中断返回时,处理器会再次执行引发异常的那条指令(而不是下一条指令),于是程序又能继续执行了。
但是,因为段的长度不定,在分配内存时,可能会发生内存中的空闲区域小于要加载的段,或者空闲区域远远大于要加载的段。在前一种情况下,需要另外寻找合适的空闲区域;在后一种情况下,分配会成功,但太过于浪费。为了解决这个问题,从80386处理器开始,引入分页机制。
分页功能从总体上来说,是用长度固定的页来代替长度不一定的段,藉此解决因段长度不同而带来的内存空间管理问题。尽管操作系统也可以用软件来实施固定长度的内存分配,但太过于复杂,由处理器固件来做这件事,可以使速度和效率最大化。
16.1 分页机制概述
【16.1.1 简单的分页模型】
在单纯的分段模式下,线性地址就是物理地址。
段页式内存管理机制:
页式内存管理把4GB内存分成大小相同的页。页的最小单位是4KB(4096字节=0x1000),页的物理地址其低12位始终为全零。4GB可以划分为1048576(0x100000)个页。
段管理机制是基本机制,分页管理机制是可选机制。
当一个程序加载时,操作系统既要在虚拟内存中分配段空间,又要在物理内存中分配相应的页面。
因此第一个步骤是寻找空闲的段空间,该段空间既没有被其他程序使用,也没有被同一程序内的其他段使用。
【如果允许页共享,多个段或多个程序可以用同一个页来存放各自的数据】
第二个步骤是根据的段长度,确定需要分配的页数量。【段必须是连续的,但不要求所分配的页都是连续的、挨在一起的。】【所分配的页面的总长度要大于等于段长度】
第三个步骤是找到空闲页后,建立线性地址和页之间的对应关系。
第四个步骤把段的数据按页的尺寸拆开,分开写入对应的页中。
从段部件输出的是线性地址,或者叫虚拟地址。为了根据线性地址找到页的物理地址,操作系统必须维护一张表,把线性地址转换成物理地址,这是一个反过程。
因为有1048576个页,所以转换表也有1048576项。这是一个一维表格,每个表项占4字节,内容为页的物理地址。这个表格的用法是这样的:因为页的尺寸是4KB,故,线性地址的低12位可用于页内偏移,高20位可用于指定一个物理页。因此,把线性地址的高20位当成索引,乘以4,作为表内偏移量,那就是该线性地址所对应的页的物理地址。再加上低12位的页内偏移量,这就是真实的物理内存地址。
为什么在表内偏移量相对应的地方就恰好是正确的页地址呢?当程序加载时,操作系统会首先在虚拟内存中分配段。然后,根据段需要分成多少页,来搜索空闲页面。当段较大时,要按页的尺寸分成好几个地址区段,操作系统用每个区段的首地址,取高20位,乘以4,作为偏移量访问表格,并将分配给该区段的页的物理地址写入该表项。最后,把原本需要写入每个区段的程序数据,写到对应的页中。
注意了,在页式内存管理中,页面的管理和分配是独立的,和分段以及段地址没有关系。操作系统所要做的,就是寻找空闲页面,把它分配给需要的段,并将页的物理地址填写到映射表内。很显然,也很重要的结论,线性地址,包括线性地址空间,和页面分配机制没有关系。
基于以上特点,同时为了充分挖掘分页内存管理的潜力,一般来说,每个任务都可以拥有4GB的虚拟内存空间;同时,每个任务都有自己的页映射表。
尽管有很多任务,而且每个任务都有自己的4GB虚拟内存空间,但是,很重要的是,在整个系统中,物理页面是统一调配的。每个任务访问同样的线性地址时,实际上访问的是不同的物理地址。
每个任务有4GB的虚拟内存空间,而物理内存只有一个,最大也才4GB,貌似不够分。事实上也的确不够分配。但是,操作系统可以将暂时不用的页退避到磁盘,调入马上就要使用的页,通过这种手段来实现分页内存管理。这就是为什么内存容量较小时,程序越来越慢,硬盘工作指示灯不停地闪烁的原因。
【16.1.2 页目录、页表和页】
第一个支持分页式内存管理模式的Intel处理器是80386。
任务的虚拟地址空间为4GB,一个页占用4KB,所以可以分出1048576个页,所以,映射表需要1048576个表项,用于存放页的物理地址。又因为每个表项占4字节,所以,映射表的总大小为4MB。这张表很大,但在实际中,没有哪个任务会真的用到所有的表项,充其量只是很小一部分,所以会很浪费。
因为一个特殊的原因,表在实际使用时前半部分和后半部分会被同时用到。因此又不可能采取先定义前部分,再根据需要实时扩展的方法。为了解决这个问题,同时又不会浪费宝贵的内存空间,处理器设计了层次化的分页结构—采用页目录表和页表(非上面的单一映射表)。首先,4GB的虚拟内存空间对应着1048576个4KB的页,可以随机地抽取这些页,将它们组织在1024个页表内。页表内每个项目叫做页表项,占4字节,存放的是页的物理地址,故每个页表的大小是4KB,正好是一个标准页的长度。页在页表内的分布是随机的。
由于页表中存放的是页的物理地址,故每个页表项占4字节,这样,每个页表占4096字节,正好是一个物理页的大小,可以很方便地使用一个物理页来定义每个页表。
1048576个页归拢到1024个页表后,再用一个表来指向1024个页表,这就是页目录表(Page Directory Table,PDT),和页表一样,页目录项的长度为4字节,填写的是页表的物理地址,共指向1024个表页,所以页目录表的大小是4KB,正好是一个标准页的长度。
每个任务都有自己的页目录和页表,在处理器内部,用控制寄存器CR3,存放着当前任务页目录的物理地址,故又叫做页目录基址寄存器(Page Directory Base Register,PDBR)。任务的TSS中有CR3寄存器域,存放了任务自己的页目录物理地址。当任务切换时,处理器切换到新任务开始执行,而CR3寄存器的内容也被更新,以指向新任务的页目录位置。相应地,页目录又指向一个个的页表,这就使得每个任务都只在自己的地址空间内运行。
页目录和页表也是普通的页。任务结束后,也会被回收,并分配给其他任务。
【16.1.3 地址变换的具体过程】
处理器的页部件专门负责线性地址到物理地址的转换工作。
1、处理器的段部件输出一个32位线性地址;
2、页部件将送来的32位线性地址截成3段,分别是高10位、中间的10位和低12位。高10位是页目录的索引,中间10位是页表的索引,低12位则作为页内偏移来用。
3、当前任务的页目录物理地址位于处理器的CR3寄存器中,从CR3寄存器中取得页目录的物理地址,将其作为基地址;将32位线性地址的高10位作为索引,乘以4,加上页目录物理地址,得到一个物理地址,从该处取得一个数值,这个数值就是当前线性地址所在页表的物理地址。
4、将32位线性地址的中间10位作为页表的索引,乘以4,再加上第3步得到的物理地址,又得到一个页表内的地址,从页表内该地址处取得一个数值,这个数值就是线性地址所在页的地址。
5、将第4步得到的页地址加上32位线性地址的低12位,得到的数值就是最终的物理地址。
这种变换不是无缘无故的,而是事先安排好的。当任务加载时,操作系统先创建虚拟的段,并根据段地址的高20位决定它要用到哪些页目录项和页表项。然后,寻找空闲的页,将原本应该写入段中的数据写到一个或者多个页中,并将页的物理地址填写到相应的页表项中。只有这样做了,当程序运行的时候,才能以相反的顺序进行地址变换,并找到正确的数据。
检测点16.1
0x0C005032的二进制形式为:
0000 1100 0000 0000 0101 0000 0011 0010
其高10位0000 1100 00,在页目录表中的偏移为0xC0,操作系统用CR3寄存器的值为基址,加上偏移0xC0,得到一个页目录表中的物理地址,并在该地址处填上一个双字0x00003000。其中间10位为00 0000 0101,在页表中的偏移为0x14,操作系统用0x00003000为基址,加上偏移0x14,得到一个页表中的物理地址0x00003014,并在该地址处填上一个双字0x0000A000。
|
|