一 页面与页表
1 页面
分页存储管理是将作业的逻辑地址划分为一系列同等大小的部分,称为页。并为各页加以编号,每个作业的页的编号都是从0开始的。与之类似,把可用的物理内存也划分为同样大小的连续的部分,称为块或页框。同样为块也进行标号,从0#开始。在为进程分配内存空间时,以页为单位,每个内存中的块存放一页用户作业。只要内存中有足够多的块,这些块可以相邻也可以不相邻,就可以存放整个作业了。
页面的大小对于内存利用和系统开销来说非常重要,页面太大,在作业的最后一页必然会剩余较大不能利用的空间--内碎片。页面太小,虽然可以减小内碎片的大小,但是一个作业的页太多,会使得作业页表太长而占用内存,同时系统频繁地进行页面转化,加重系统开销。因此,页面的大小应该适中,通常为512B
- 8KB,windows系统的页面大小为4KB。
2 地址结构
分页系统中的地址结构由两部分组成,页号和页内偏移量。可以解释为一个二元组(p,w),其中p是页号,w是页面p中的偏移量或者相对于p页开始的位置。下图(a) 中的地址长度为32位,其中0 - 9位为页内偏移量,每页的大小为2的10次方
= 1k;10 - 31位为页号,共计2的22次方 = 4M页。在图(b)中,地址长度同样为32位,其中0 - 11位页内偏移量,每页的大小为2的12次方 = 4k;12 - 31位为页号,共计2的20次方 = 1M页,由此可知不同的系统页的大小是不一样的。
(a)页面大小为1KB(2的10次方)
(b)页面大小为4KB(2的12次方)
对于特定的机器来说,其地址结构是一定的。若给定逻辑地址A,页面大小为L,则页号p和页内偏移量w分别为
p = INT [A/L] w = [A]MODL
例如:系统的页面大小事1K,设A = 3096,则由上式得出 p =3,w =24。
3 页表
在分页存储管理中,页的存放可以是连续的,也可以是不连续的,这就增加了逻辑地址到物理地址转换的难度。如何在内存中找到页所对应的物理块是地址装换的关键。为此,系统为每个进程创建了一个页表。在进程逻辑地址空间中的每一页,依次在页表中有一个表项,记录了该页对应的物理块号。如下图所示
在配置了页表之后,通过查找页表就可以很容易地找到该页在内存中的位置。页表具有逻辑地址到物理地址映射的作用。
对于页的保护通常设置一个存取控制字段。当这个字段占一位时,用于规定该页中的内容允许写还是读;如果存取控制字段占两位,那么它可以表示存取控制为读写、只读和只运行三种。当进程写一个只读页时,系统就会通过中断来报错。
二 地址变换结构
为了实现分页管理逻辑地址到物理地址的转换,系统中必须设置地址变换机构,用来实现地址映射。由于页的大小和块的大小是一样的,当把进程的某一页放入内存时,该页内地址的页偏移量和块内偏移量是一致的,因此地址转换时就不必考虑偏移量,只考虑逻辑页号和实际物理号的对应即可。页表中存放的就是页号和其对应的物理块号,所以地址变换就要借助页表来完成。
1 基本地址变换
地址变换的第一步就是检索页表。为了实现快速的检索页表,最好把页表放在寄存器中,每一个表项都用一个寄存器。但是有一个问题,通常计算机中的寄存器都不多,而页表可能非常大,现代计算机的虚拟地址至少是32位的,比如,页的大小为4KB,那么32位的地址空间将有1M个页面,64位的地址空间则更多。虚拟空间中的1M个页面需要1M个表项。并且,每个进程都有自己的页表。因此,页表通常存放在内存中。在系统中只设置一个页表寄存器,其中存放页表的开始地址和页表长度。平时进程未执行时,页表的开始地址和页表的长度放在PCB中,当进程运行时,把这两个数据装入页表寄存器中。
当进程要访问某个地址中的数据时,地址变换机构首先自动地将地址转换成页号和页内偏移量,然后根据页号来检索页表。在检索之前要判断页号是否大于等于页表长度,如果页号大于等于页表长度,说明超出了有效地址范围,于是产生一个错误中断。否则,把页号和页表项长度相乘得到的结果与页表开始地址相加,就得到了该页表项在页表中的地址,从而找到对应的物理块号,把物理块号装入物理地址寄存器中,同时把页内偏移量送入物理地址寄存器对应的块内偏移量中,由此得到真正的物理地址。
由于页表是放在内存中的,那么一次数据访问需要两次访问内存,第一次访问页表,找到对应的物理号,然后与偏移量拼接形成物理地址;第二次从第一次得到的物理地址结构中访问数据。系统的运行速度一般都受到CPU从内存中取得指令和数据的速率的限制,一次数据两次访问内存会使计算机的处理速度降低50%。如何有效的解决这个问题?
采取的解决方法是在地址变换结构中增加一个具有并行查找能力的特殊的高速缓冲寄存器,这种设备称为转换检测缓冲区,又称为快表,用于存放当前访问过的页表项。此时,当给出一个有效地址时,地址变换机构首先通过将该页号p同TLB中的所有表项同时进行比较,判断该表是否在其中,如果发现可匹配的页面,则直接取出其页表项得到物理块号,而不必通过页表。如果地址变换机构没有可匹配的项,就进行正常的页表查询。首先从TLB中淘汰一个表项,然后用新找到的页表项替换它。这样,如果这一页很快再次被访问,那么第二次自然将会命中。
因为寄存器的价格原因,快表的结构不可能很大,通常能存放16 - 512个页表项,这对中小型作业来说,有可能把全部页表放入快表中,对于大型作业,可以将常用的页表项放入其中。由于程序的局部性原则,快表的引入极大改善了系统的效率,数据显示,从快表中查找到页表项的概率可以达到90%。这样因为访问页表而访问内存的次数就会大大减少,从而降低系统的开销。
2 多级页表
现代的计算机都有非常大的逻辑地址空间,以32位计算机为例,假设页的大小为4KB,那么一个作业的页最多可以达到2的20次方个,这意味着该作业的页表现为2的20次方。假设一个页表现占用一个字节,那么该页表的大小为2的20次方B,即需要1MB的内存空间。并且要求者1MB的内存空间是连续的。这显然是不现实的,解决这个问题的最好方法是:把页表页看成普通的文件,对它进行离散分配,即对页表再分页,由此形成多级页表的思想。
以二级页表为例,将页表进行分页后,离散的存放在不同的物理块中,这样,对这些离散分配的页表再建立页表,即二级页表。在下图中,32位的虚地址划分成10位的外层页表域,10位的内层页表域和12位的页内偏移量。
32位逻辑地址空间使用两级页表映射到32位物理地址空间,每个页面大小为4KB。一级页表的开始物理地址被存放在页表基址寄存器中,一级地址映射使用逻辑地址的最高10位来索引,并产生第二级页表的物理地址。下面10位用来索引第二级页表,产生出的物理页的地址和逻辑地址的最低12位相结合以生成物理地址。如果某二级页表中没有实际映射,就可将其删除并在顶级页表中标记为不可用。许多分页方案在构造的时候都使各级页表的大小和页的大小一致,这样存储它们占用的空间可以和进程使用的内存页使用相同的分配方案。
对于32位的机器,采用二级页表是合适的;但对于64位的机器,采用二级页表是不合适的,因此必须采用多级页表。
分页存储管理方式虽然可以解决程序和数据无需连续存储空间的问题,但是这种内存管理方式依然要求整个作业都要装入内存运行,既没有解决大作业小内存的问题,此外,该种管理方式纯粹从存储的角度去考虑,没有考虑到程序本身的存储问题。
说明总结:单页时,由于页表项是连续的(对应着物理地址空间),造成必须占用一整块连续地址空间,是不可取的,所以采用多页表(主要是二级页表),但造成整个逻辑块被分散。
基本分段分配方式
分页存储管可以实现内存利用率的提高,但是分页的纯物理解决方案,一个逻辑段被离散的放在很多个物理块中。很多时候,程序员希望把一个程序按照它的逻辑结构存放在内存中。
一个程序的逻辑段在程序运行过程中有的大小会发生变化,如数据段和堆栈;而有的逻辑段的大小在运行过程中不发生变化,如代码段。在分页存储管理方式中,对于随时动态增长的段的存储管理是非常困难的,一旦断的增长涉及重新分配物理块,那么就涉及页表的修改等问题。
一个具有n个过程的程序,在分页存储管理中,过程被一个一个紧紧地放在一起,中间没有间隙,结果是修改一个过程的大小会影响其他进程的起始地址,进而又需要修改所有调用被移动进程的进程,以使它们的访问指向这些过程的新地址。在一个有数百个过程的程序中,这个操作的开销是相当大的。
在分页存储管理中,一个逻辑段可能存放在n个物理块中,如果几个程序共享这个逻辑段就需要早每个程序的页表中添加n项,以便实现地址转换。这样势必会增加页表的大小,进而增加系统的开销,对于逻辑页的保护也是同样的道理。
针对这些问题,一个非常通用的办法是为一个程序提供多个相互独立的称为段的地址空间。每个段由一个从0到最大的线性地址序列构成。各个段的长度可以是0到某个允许的最大值之间的一个值。不同段的长度可以不同,而且通常也不同。段的长度在运行期间可以改变,堆栈段的长度在数据被压入时会增长。在数据被弹出时又会减小。
因为每个段都是一个独立的空间,它们可以独立地增长或减小而不会影响到其他的段。段是一个逻辑实体,一个段可能包括一个过程,一个数组,一个堆栈,一组数值变量,但一般它不会同时包含多种不同类型的内容。分段分配方式实现段长度的扩充,段的动态链接以及段的保护和共享都比页式存储容易实现。
1 段表
在分段存储管理中,逻辑地址结构是二维的,即段号和段内偏移量。如下图所示。在该地址结构中,允许一个作业最长有64K个段,每个段的最大长度为64KB。
在分段分配方式中,系统为每个分段分配一个连续的分区,进程中的各个段可以离散地装入内存中的不同分区中。为了实现逻辑地址到物理地址的转换,在系统中为每个进程建立一张段表。每个段在段表中都占有一个表项,其中记录了该段的起始地址和段的地址,如图所示,段表可以存放在一组寄存器中,这样有助于提高地址转换速度;但更常见的是放在内存中。在配置了段表之后,执行中的进程可以通过查找段表找到每个段所对应的内存区。
2 采用大小不等的段,逻辑地址和物理地址之间不再是简单的对应关系。考虑一个 n+m 位的地址,左边的n位为段号,右边的m位为段内地址。当进程进入运行状态时,它的段表地址被装入到段表寄存器中。