操作系统:离散分配存储管理方式
离散分配方式
连续分配方式会形成许多外部碎片,虽然可通过“紧凑”方法将许多碎片拼接成可用的大块空间,但紧凑的时间开销和复杂度都很高。如果允许将一个进程直接分散地装入到许多不相邻接的分区中,便可充分地利用内存空间,也就是采用离散分配的方式。根据在离散分配时所分配地址空间的基本单位的不同,又可将离散分配分为以下三种:
- 分页存储管理方式:将用户程序的地址空间分为若干个固定大小的“页面”,也将内存空间分为若干个页框,将用户程序的任一页放入任一物理块中;
- 分段存储管理方式:把用户程序的地址空间分为若干个大小不同的段,以段为单位进行分配,这些段在内存中可以不相邻接;
- 段页式存储管理方式:将分页和分段两种存储管理方式相结合。
分页存储管理
分页存储的结构
分页存储管理将进程的逻辑地址空间分成若干个页面,并为各页加以编号。相应地也把内存的物理地址空间分成若干个物理块,同样也加以编号。在为进程分配内存时,以块为单位,将进程中的若干个页分别装入到多个可以不相邻接的物理块中。若页面大小设置得较小,可以减小内存碎片并提高内存利用率,但会造成每个进程占用较多的页面,从而导致进程的页表过长、占用大量内存和降低页面换进换出的效率。如果选择的页面过大,虽然可以减少页表的长度,提高页面换进换出的速度,但却又会使页内碎片增大。因此页面的大小应选择适中,且页面大小应是 2 的幂,通常为 1KB~8KB。
分页地址中的地址长度为 32 位,地址结构包含两部分内容。前一部分为页号 P,占在 12 ~ 31 位,地址空间最多允许有 1M 页。后一部分为位(偏)移量 W(页内地址),占在 0 ~ 11 位,每页的大小为 4KB。
对某特定机器的地址结构是一定的,若给定一个逻辑地址空间中的地址为 A,页面的大小为 L,则页号 P 和页内地址 d 可按下式求得,其中 INT 是整除函数。例如系统的页面大小为 1KB,设 A = 2170B,则由公式可以求得 P = 2,d = 122。
在分页系统中,允许将进程的各个页离散地存储在内存的任一物理块中。为了能在内存中找到每个页面所对应的物理块,系统又为每个进程建立了一张页面映像表实现从页号到物理块号的地址映射,简称页表。在进程地址空间内的所有页(0 ~ n),依次在页表中有一页表项,其中记录了相应页在内存中对应的物理块号。进程执行时通过查找该表,即可找到每页在内存中的物理块号。
基本的地址变换机构
为了能将用户地址空间中的逻辑地址转换为内存空间中的物理地址,在系统中必须设置地址变换机构。由于页内地址和物理地址是一一对应的(例如对于页面大小是 1KB 的页内地址是 0 ~ 1023,其相应的物理块内的地址也是 0 ~ 1023),因此地址变换机构的任务实际上只是将逻辑地址中的页号转换为内存中的物理块号。
页表大多驻留在内存中,在系统中只设置一个页表寄存器 PTR(Page-Table Register),存放页表在内存的始址和页表的长度。平时,进程未执行时,页表的始址和页表长度存放在本进程的 PCB 中。当调度程序调度到某进程时,才将这两个数据装入页表寄存器中。
当进程要访问某个逻辑地址中的数据时,分页地址变换机构会自动地将有效地址 A(相对地址)分为页号 P 和页内地址 W 两部分。在执行检索之前,先将页号与页表长度进行比较。如果页号大于或等于页表长度,则表示本次所访问的地址已超越进程的地址空间,需要产生地址越界中断。若未出现越界错误,则将页表始址 F 与页号 P 和页表项长度 M 的乘积相加,便得到该表项在页表中的位置,进而从页表得到该页的物理块号。将块号和页内地址送入物理地址寄存器的块内地址字段中,完成从逻辑地址到物理地址的变换。
例如若页面大小 L 为 1K 字节,页号 2 对应的内存块号 b = 8,要将逻辑地址 A = 2500 转换为物理地址 E。页面大小为 1K 字节,说明一个页面的大小为
2^10B = 1KB,首先根据公式计算出页号、页内偏移量:
页号 P = A / L = 2500 / 1024 = 2
页内偏移量 W = A % L = 2500 % 1024 = 452
根据题中条件可知,页号 2 没有越界,其存放的内存块号 b = 8,即可计算出物理地址。
物理地址 E = b * L + W = 8 * 1024 + 425 = 8644
在分页存储管理(页式管理)的系统中,只要确定了每个页面的大小,逻辑地址结构就确定了。因此只要给出一个逻辑地址,系统就可以自动地算出页号、页内偏移量两个部分,并不需要显式地告诉系统。
反置页表
反置页表的引入
在分页系统中为每个进程配置了一张页表,进程逻辑地址空间中的每一页在页表中都对应有一个页表项。在现代计算机系统中,通常允许一个进程的逻辑地址空间非常大,因此就需要有许多的页表项,而因此也会占用大量的内存空间。为了减少页表占用的内存空间,引入了反置页表。反置页表(Inverted Page Table)则为每一个物理块设置一个页表项,并将它们按物理块的编号排序,其中的内容则是页号和其所隶属进程的标识符。
地址变换
在利用反置页表进行地址变换时,是根据进程标识符和页号检索反置页表。如果检索到与之匹配的页表项,则该页表项(中)的序号 i 便是该页所在的物理块号,可用该块号与页内地址一起构成物理地址送内存地址寄存器。若检索了整个反置页表仍未找到匹配的页表项,则表明此页尚未装入内存。对于不具有请求调页功能的存储器管理系统,此时则表示地址出错。
然而在该表中只包含了已经调入内存的页面,并未包含尚未调入内存的页面,因此还必须为每个进程建立一个外部页表。(External Page Table)。该页表与传统的页表一样,当所访问的页面在内存时,并不需要访问外部页表,仅当发现所需之页面不在内存时才使用。在页表中包含了各个页在外存的物理位置,通过它可将所需之页面调入内存。
由于在反置页表中是为每一个物理块设置一个页表项,当内存容量很大时,页表项的数目还是会非常大的。要利用进程标识符和页号去检索这样大的一张线性表是相当费时的,可利用 Hash 算法来进行检索。
快表
具有快表的地址变换机构
由于页表是存放在内存中的,这使 CPU 在每存取一个数据时都要两次访问内存。第一次是访问内存中的页表找到物理块号 b,再将块号与页内偏移量 W 拼接形成物理地址,第二次访问内存时,才是从所得地址中读写数据。
为了提高地址变换速度,可在地址变换机构中增设一个具有并行查寻能力的特殊高速缓冲寄存器,又称为“联想寄存器”(Associative Memory)或“快表 TLB(Translation Look aside Buffer)”。在 CPU 给出有效地址 A 后,由地址变换机构自动地将页号 P 送入 TLB 并和其中所有页号进行比较。若其中有与此相匹配的页号,便表示所要访问的页表项在 TLB 中,就可直接从快表中读出对应的物理块号 b。如在快表中未找到对应的页表项,则还须再访问内存中的页表,找到后把从页表项中读出的物理块号 b 送往地址寄存器,同时再将此页表项存入 TLB 的一个寄存器单元中。但如果联想寄存器已满,则 OS 必须找到一个被认为是不再需要的页表项将它换出。
由于查询快表的速度比查询页表的速度快很多,因此只要快表命中就可以节省很多时间。虽然快表的容量有限,但是一般来说快表的命中率可以达到 90% 以上。
访问内存的有效时间
从进程发出指定逻辑地址的访问请求,经过地址变换,到在内存中找到对应的实际物理地址单元并取出数据,所需要花费的总时间称为内存的有效访问时间(Effective AccessTime,EAT)。
假设访问一次内存的时间为 t,在基本分页存储管理方式中,有效访问时间分为 2 次访问内存时间之和:
EAT = t + t = 2t
在引入快表的分页存储管理方式中,如果快表命中可以直接得到逻辑页所对应的物理块号,减少了一次内存访问。命中率是指使用快表并在其中成功查找到所需页面的表项的比率,引入 TLB 的分页存储管理方式的 EAT 计算公式如下,其中 λ 表示查找快表所需要的时间,a 表示命中率,t 表示访问一次内存所需要的时间。
EAT = a × λ + (t + λ)(1 - a) + t
= 2t + λ - t x a
例如某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问一次快表耗时 1us,访问一次内存耗时 100us,若快表的命中率为 90%,访问一个逻辑地址的平均耗时通过计算可以得到。
快表和慢表分开查找:(1 + 100) * 0.9 + (1 + 100 + 100) * 0.1 = 111 us
快表和慢表同时查找:(1 + 100) * 0.9 + (100 + 100) * 0.1 = 110.9 us
若未采用快表机制,则访问一个逻辑地址需要 100 + 100 = 200us,显然引入快表机制后访问一个逻辑地址的速度被加快。
两级和多级页表
引入多级页表的动机
现代的大多数计算机系统都支持非常大的逻辑地址空间,导致页表要占用相当大的内存空间。例如对于一个具有 32 位逻辑地址空间的分页系统,规定页面大小为 4KB,则在每个进程页表中的页表项数可达 1MB。又因为每个页表项占用一个字节,故每个进程仅仅的页表就要占用 1MB 的连续的内存空间。
引入多级页表是为了使用离散分配方式,以解决难以找到一块连续的大内存空间的问题。同时实现只将当前需要的部分页表项调入内存,其余的页表项仍驻留在磁盘上,需要时再调入。
两级页表
为了解决进程在内存中的连续存储问题,做法是将进程地址空间分页,并为其建立一张页表,记录各页面的存放位置。同样的思路也可用于解决页表存放的问题,把必须连续存放的页表再分页,使每个内存块刚好可以放入一个分组。比如页面大小 4KB,每个页表项 4B,每个页面可存放 1K 个页表项。因此每 1K 个连续的页表项为一组,每组刚好占一个内存块,再讲各组离散地放到各个内存块中。这种结构为两级页表(Two-Level Page Table),实现时要为离散分配的页表再建立一张页表,称为页目录表或外层页表或顶层页表。
在页表的每个表项中,存放的是进程的某页在内存中的物理块号,而在外层页表的每个页表项中所存放的是某页表分页的首址。在地址变换机构中同样需要增设一个外层页表寄存器,用于存放外层页表的始址。地址转换时首先按照地址结构将逻辑地址拆分成三部分,接着从 PCB 中读出页目录表始址,再根据一级页号查页目录表,找到下一级页表在内存中的存放位置。然后根据二级页号查表,找到最终想访问的内存块号,最后结合页内偏移量得到物理地址。
两级页表的访存次数为 3 次,第一次访存访问内存中的页目录表,第二次访存访问内存中的二级页表,第三次访存访问目标内存单元。
此时可以实现可以在需要访问页面时才把页面调入内存,通过在页表项中增加一个标志位,用于表示该页面是否已经调入内存。若想访问的页面不在内存中,则产生缺页中断(内中断),然后将目标页面从外存调入内存。
多级页表
当使用的计算机的位数不同时,可能会继续拓展到多级页表,将外层页表再进行分页,将各分页离散地装入到不相邻接的物理块中,再利用第 2 级的外层页表来映射它们之间的关系。若采用多级页表机制,则各级页表的大小不能超过一个页面。例如某系统按字节编址,采用 40 位逻辑地址,页面大小为 4KB,页表项大小为 4B,假设采用纯页式存储,则要采用几级页表?页内偏移量为几位?
首先页面大小 = 4KB = 2^12B,按字节编址的页内偏移量为 12 位。页号 = 40 - 12 = 28 位,页面大小 = 2^12B,页表项大小 = 4B,则每个页面可存放 212 / 4 = 210 个页表项。此时需要 10 位二进制位才能映射到 210 个页表项,因此每一级的页表对应页号应为 10 位。总共 28 位的页号至少要分为三级。
分段存储管理
引入分段存储管理的动机
引入分段存储管理方式主要有 2 方面的原因,一方面是由于通常的程序都可分为若干个段,每个段大多是一个相对独立的逻辑单位。另一方面实现和满足信息共享、信息保护、动态链接以及信息的动态增长等需要,也都是以段为基本单位的。分段存储管理方式更符合用户和程序员的需要,具体的需求有如下几点。
需求 | 说明 |
---|---|
方便编程 | 程序员访问的逻辑地址是由段名(段号)和段内偏移量(段内地址)决定的 |
信息共享 | 在实现对程序和数据的共享时是以信息的逻辑单位为基础的,以段为单位更利于信息共享 |
信息保护 | 信息保护同样是以信息的逻辑单位为基础的,以段为单位更利于信息保护 |
动态增长 | 在应用中存在着一些段,在使用过程中所需要的存储空间需要动态增加 |
动态链接 | 当需要调用某个目标程序时才将其调入内存并进行链接,动态链接以段作为链接的基本单位 |
分段存储管理的基本结构
分段
作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息。通常可用一个段号来代替段名,每个段都从 0 开始编址,并采用一段连续的地址空间。段的长度由相应的逻辑信息组的长度决定,因此各段的长度并不相等。整个作业的地址空间由于被分成多个段,其逻辑地址由段号(段名)和段内地址所组成。
分段方式已得到许多编译程序的支持,编译程序能自动地根据源程序的情况产生若干个段。
段表
在分段式存储管理系统中,内存为每个分段分配一个连续的分区,进程中的各个段可以离散地装入内存中不同的分区中。为保证程序能正常运行,就必须能从物理内存中找出每个逻辑段所对应的位置,为此需为每个进程建立一张段映射表。每个段在表中占有一个表项,记录了该段在内存中的起始地址(基址)和段的长度。在配置了段表后,执行中的进程可通过查找段表,找到每个段所对应的内存区。
各个段表项的长度是相同的,例如某系统按字节寻址且采用分段存储管理,逻辑地址结构为段号 16 位、段内地址 16 位),因此用 16 位即可表示最大段长。物理内存大小为 4GB,因此可以让每个段表项占 16 + 32 = 48 位(6B)。由于段表项长度相同,因此段号可以是隐含的,不占存储空间。若段表存放的起始地址为 M,则 K 号段对应的段表项存放的地址为 M + K * 6。
分段地址变换机构
为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器存放段表始址和段表长度 TL。在进行地址变换时,系统将逻辑地址中的段号 S 与段表长度 TL 进行比较。若 S > TL 表示段号太大访问越界,于是产生越界中断信号。若未越界则根据段表的始址和该段的段号,计算出该段对应段表项的位置,从中读出该段在内存的起始地址。然后再检查段内地址 d 是否超过该段的段长 SL 。若 d>SL 同样发出越界中断信号,若未越界则将该段的基址 d 与段内地址 W 相加,得到要访问的内存物理地址。
像分页系统一样,当段表放在内存中时,每要访问一个数据都须访问两次内存。为了提高访问速度也增设一个联想存储器,用于保存最近常用的段表项。
段页式存储管理方式
分段和分页管理的异同
分页和分段系统都采用离散分配方式,且都是通过地址映射机构实现地址变换。两者完全不同主要表现在下述三个方面:
- 页是信息的物理单位,采用分页存储管理方式是为提高内存的利用率,仅仅只是系统管理上的需要,对用户是不可见的。分段存储管理方式中的段则是信息的逻辑单位,它通常包含的是一组意义相对完整的信息,是为了能更好地满足用户的需要;
- 页的大小固定且由系统决定,在采用分页存储管理方式的系统中,在硬件结构上就把用户程序的逻辑地址划分为页号和页内地址两部分。段的长度却不固定,决定于用户所编写的程序;
- 分页的用户程序地址空间是一维的,用户程序的地址是属于单一的线性地址空间。在分段系统中用户程序的地址空间是二维的,程序员在标识一个地址时需给出段名和段内地址。
段页式管理的原理
分页系统以页面作为内存分配的基本单位,能有效地提高内存利用率,而分段系统以段作为内存分配的基本单位,它能够更好地满足用户多方面的需要。如果能对两种存储管理方式“各取所长”,则可形成段页式存储管理方式。这种新的系统既具有分段系统的便于实现、分段可共享、易于保护、可动态链接等优点,又能像分页系统那样解决内存的外部碎片问题。
段页式系统的基本原理是先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。在段页式系统中,其地址结构由段号、段内页号及页内地址三部分所组成。段号的位数决定了每个进程最多可以分几个段,页号位数(段内页号)决定了每个段最大有多少页,页内偏移量(页内地址)决定了页面大小、内存块大小是多少。
例如若系统是按字节寻址的,段号占 16 位,因此在该系统中每个进程最多有 2^16 = 64K 个段。页号占 4 位,因此每个段最多有 2^4 = 16 页。页内偏移量占 12 位,因此每个页面\每个内存块大小为 2^12 = 4096 = 4KB。
在段页式系统中,为了实现从逻辑地址到物理地址的变换,系统中需要同时配置段表和页表。段表的内容不再是内存始址和段长,而是页表始址和页表长度。
地址变换过程
在段页式系统中须配置一个段表寄存器,其中存放段表始址和段长 TL。进行地址变换时首先利用段号 S,将它与段长 TL 进行比较。若 S < TL 表示未越界,于是利用段表始址和段号来求出该段所对应的段表项在段表中的位置,从中得到该段的页表始址。利用逻辑地址中的段内页号 P 来获得对应页的页表项位置,读出物理块号b,再利用块号 b 和页内地址来构成物理地址。
在段页式系统中,为了获得一条指令或数据须三次访问内存。第一次访问是访问内存中的段表,从中取得页表始址。第二次访问是访问内存中的页表,从中取出该页所在的物理块号,并将该块号与页内地址一起形成指令或数据的物理地址。第三次访问才是真正从第二次访问所得的地址中,取出指令或数据。这使访问内存的次数增加了近两倍。为了提高执行速度,在地址变换机构中增设一个高速缓冲寄存器。
参考资料
《计算机操作系统(第四版)》,汤小丹 梁红兵 哲凤屏 汤子瀛 编著,西安电子科技大学出版社