计算机系统中的存储器分为:内存储器和辅助存储器 内存储器可被CPU直接访问,辅助存储器不能被CPU直接访问 辅助存储器与CPU之间只能在输入输出控制系统的管理下,进行信息交换。 在操作系统中把管理内存储器的部分称为“存储管理” 是否合理地使用内存,会在很大程度上影响到整个计算机系统的性能
存储管理综述
存储器的层次结构
计算机采用以存储器为中心的体系结构
存储器复杂存放整个系统的程序与数据,是重要的系统资源
在这种层次结构中,CPU可以直接到寄存器、高速缓冲存储器、内存储器3层上访问数据
不能直接到磁盘和磁带上访问数据,只能先读入内存储器后,才能接受CPU的处理
高速缓冲存储器的工作原理
CPU的工作速度总是快于存储器的访问速度,CPU速度的提高也总是快于存储器访问速度的提高。
这种深度上的不匹配,制约了系统整体性能的发挥
若用寄存器组成内存储器,那么这种内存储器的访问速度肯定会很快。
但是,寄存器的价格昂贵,完全由它来组成内存储器是极不现实的想法。
因此在 CPU 与内存储器之间就出现了所谓的“高速缓冲存储器”,通常简称为“高速缓存”。
高速缓冲存储器比内存储器容量小,存取速度快。只存放内存中的一小部分数据内容
CPU读取字时,总是先检查字是否在高速缓存中,如果在就直接将它给CPU;如果不在则把内存中的这个字的数据读入高速缓存,再把字从高速缓存传送给CPU。
内存储器和高速缓存之间是以“块”为单位传递数据的,高速缓存与CPU之间则是以“字”为单位传递数据的。
比如,内存储器由2n个字组成,每个字有一个唯一的n位地址。将内存储器按照每块K个字的大小划分成M=2n/K个块。高速缓存中有C个存储槽,每个槽里可容纳K个字。存储槽的数目远远小于内存储器中块的数目(即C<<M)。这样,内存储器中只可能有若干块存放在高速缓存的存储槽中。
当CPU需要存取内存储器中某块里的某字时,如果那块当时不在存储槽中,就把那块传送到一个槽里。由于槽的数目远远小于块的数目,因此一个存储槽不可能唯一或永远对应于某一块。所以,高速缓存中的每个槽都有一个标签,用来标识这个存储槽在当前存放的是内存中的哪一块。
存储管理的功能
- 内存的分配与回收
内存空间的分配与回收,是存储管理必须承担的任务。无论采用哪一种存储管理策略,它都应该随时记录内存空间的使用情况;根据用户程序的需要分配存储区;在用户程序运行完后,及时收回存储区,以便提高内存空间的使用效率。
- 存储保护和共享
存储保护涉及两个方面的问题,一是要确保用户进程的程序不得侵犯操作系统;二是要确保这个用户程序和那个用户程序之间不能相互干扰。为此,在存储管理中,必须得到硬件的支持,才能实现存储保护所要求达到的目标。 存储共享是指允许多个进程程序访问内存中的同一个部分,这是提高存储利用率的一种措施。存储管理必须允许对某个内存区域的共享,又要不伤害存储保护的利益。
- 地址定位
为了适应多道程序设计环境,为了使内存中的程序能在内存中移动,操作系统的存储管理必须提供实施地址重定位的方法。对用户程序逻辑地址空间中的地址实施重新定位,以保证程序的正确运行。
- 存储扩充
存储扩充的含义是通过一定的技术手段,给用户造成有一个非常大的内存空间的虚幻感觉,但其实并没有扩大实际内存的容量。存储管理若能做到这种意义下的存储扩充,就能使用户程序的规模不受内存实际容量的限制。存储扩充无疑是一件非常好的事情。
固定分区存储管理
地址重定位
内存储器由一个个存储单元组成。
一个存储单元可存放若干个二进制位(bit),8个二进制位被称作一个字节(Byte)
内存中的存储单元按一定顺序进行编号,每个单元所对应的编号,称为该单元的单元地址
一个单元的单元地址具有唯一性,存储在单元里的内容则是可以改变的。
在操作系统中,常把单元地址称为内存储器的“绝对地址”或“物理地址”。
从任何一个绝对地址开始的一段连续的内存空间,被称为“绝对地址空间”,或“物理地址空间”。
在多道程序设计环境下,用户无法事先指定要占用内存的哪个区域,也不知道自己的程序将会放在内存的什么地方。
假定用户程序A的相对地址空间为0~3KB(0~3071),在该程序中地址为3000的地方,有一条调用子程序(其入口地址为100)的指令:“call 100”
很明显,用户程序指令中出现的都是相对地址,即都是相对于“0”的地址。若当前操作系统在内存储器占用0~20KB的存储区,把程序A装入到内存储器中20KB往下的存储区域中,那么,它这时占据的是内存储器中20KB~23KB的区域,这个区域就是它的绝对地址空间。现在它还不能正确运行,因为在执行到位于绝对地址23480(20KB+3000)处的“call 100”指令时,因为没有经过地址重定位,CPU就会到绝对地址为100的对方去调用所需的子程序,而这个地址却在操作系统里面,它不是人们真正需要的程序的入口地址。
之所以出现这样的错误,是因为call后面所跟随的子程序入口地址现在应该是20580,而不应该保持原来的100。这表明,当一个程序装入内存后,如果不将其指令中涉及的一个个地址进行调整,让它们反映出当前所在的存储位置,那么执行时势必会引起混乱,造成不可收拾的局面。
在操作系统中,把用户程序指令中的相对地址变为所在绝对地址空间中的绝对地址的这个过程,称为“地址重定位”。也就是说,把指令“call 100”中的100变换成20580,就是地址重定位。
地址重定位与用户程序当前占用的绝对地址空间的起始地址有关。
把程序A装入(20KB~23KB)绝对地址空间里,因此 call 指令中相对地址100所对应的绝对地址是20KB+100 = 20580
把程序 A 装入(22KB~25KB)的绝对地址空间里,那么call指令中相对地址100所对应的绝对地址就应该是22KB+100 = 22628
地址的定位方式和静态重定位
根据对指令中地址实行定位时间的不同,有3种不同的地址定位方式:
- 绝对定位方式
- 静态重定位方式
- 动态重定位方式
绝对定位方式 所谓“绝对定位”,即是在程序装入内存之前,程序指令中的地址就已经是绝对地址,已经正确地反映了它将要进入的存储区位置。 早期程序设计时,程序设计人员直接在程序中使用实际的物理地址;或者在经汇编程序、编译程序翻译时,就转换成为实际的物理地址。这就是绝对定位 优点:程序中的逻辑地址与实际内存中的物理地址完全相同,所以在程序执行之前,不需要对程序中的地址进行任何调整和修改,装入指定内存位置就可以运行 缺点:
- 要求编程人员熟悉内存的使用情况,要非常小心地对待指令中的地址,不能出现任何差错,否则后果不堪设想
- 程序进入内存后,不能做任何移动,只能固定在这个存储区内
- 对程序所作的微小修改,都可能会牵扯到程序整体的变动,耗时耗力
- 不适合多道程序设计环境
静态重定位方式 在多道程序设计环境下,用户事先无法、也不愿意知道自己的程序会被装入到内存的什么位置,他们只是向系统提供相对于“0”编址的程序。因此,系统必须有一个“重定位装入程序”,它的功能有3个:
- 根据当前内存的使用情况,为欲装入的二进制目标程序分配所需的存储区
- 根据所分配的存储区,对程序中的指令地址进行重新计算和修改
- 将重定位后的二进制目标程序装入到指定的存储区中
使用这种重定位方式,用户向装入程序提供相对于“0”编址的二进制目标程序,无需关注程序具体的装入位置 通过重定位装入程序的加工,目标程序就进入到了分配给它的物理地址空间,程序指令中的地址也都被修改为正确反映该空间的情形。 这种地址重定位是在程序执行前完成的,常被称为是地址的“静态重定位”或“静态地址绑定”。 特点:
- 由软件(重定位装入程序)实现,无需硬件提供支持
- 在程序运行之前完成地址重定位工作的
- 物理地址空间里的目标程序与原逻辑地址空间里的目标程序面目已不相同,前者是后者进行地址调整后的结果
- 实施静态重定位后,位于物理地址空间里的用户程序不能在内存中移动,除非重新进行地址定位
- 适用于多道程序设计环境
动态重定位方式 在实施存储管理时,为了能够将分散的小空闲存储块合并成一个大的存储块,却经常需要移动内存中的程序。因此,就产生了将地址定位的时间推迟到程序执行时再进行的、所谓的地址“动态重定位”方式。 在对程序实行动态重定位时,需要硬件的支持。硬件中要有一个地址转换机构,它由地址转换线路和一个“定位寄存器”(也称“基址寄存器”)组成。这时,用户程序被不做任何修改地装入到分配给它的内存空间中。 当调度到程序运行时,就把它所在物理空间的起始地址加载到定位寄存器中。CPU 每执行一条指令,就把指令中的相对地址与定位寄存器中的值相“加”,得到绝对地址。然后按照这个绝对地址去执行指令,访问所需要的存储位置。
单一连续分区存储管理
早期计算机或个人微机而言,每次只有一个用户使用计算机,无从涉及多道程序设计,因此,在这些机器上运行的操作系统,其存储管理都采用单一连续分区的分配策略。
基本思想:总体上把内存储器分为两个分区。其中的一个分区被指定分配给操作系统使用;另一个分区被分配给用户使用,称为“用户区”。
特点:
- 系统总是把整个用户区分配给一个用户使用
- 实际上,内存用户区又被分为“使用区”和“空闲区”两部分。使用区是用户作业程序真正占用的那个连续存储区域;空闲区是分配给了用户、但未被使用的区域。在操作系统中,把分配给了用户但未被使用的区域称为“内部碎片”。内部碎片的存在是对内存资源的一种浪费。
- 由于任何时刻内存储器的用户区中只有一个作业运行,因此这种系统只适用于单用户(或单道)的情况。
- 进入内存的作业,独享系统中的所有资源,包括内存中的整个用户区。
- 由于整个用户区都分配给了一个用户使用,因此作业程序进入用户区后,没有移动的必要。采用这种存储分配策略时,将对用户程序实行静态重定位。
实行静态重定位,并不能阻止用户有意无意地通过不恰当的指令闯入操作系统所占用的存储区域。因此出现了“存储保护”的问题。 在单一连续分区存储管理中,为了有效阻止用户程序指令中的地址闯入操作系统所占用的区域,将在CPU中设置一个用于存储保护的专用寄存器——“界限寄存器”。在界限寄存器中,总是存放着内存用户区的起始地址。当CPU在管态下工作时,允许访问内存中的任何地址;当CPU在目态下工作时,对内存储器的每一次访问,都要在硬件的控制下与界限寄存器中的内容进行比较。一旦发现所访问的地址小于界限寄存器中的地址,就会产生“地址越界”中断,阻止这次访问的进行,从而将作业限制在规定的存储区域内运行,确保被保护区中的信息不受外来破坏。 缺点:
- 每次只能有一个作业进入内存,不适用于多道程序设计,工作效率不高,资源利用率低
- 作业比用户区小时就会形成碎片,导致内存储器资源浪费
- 无法运行相对地址空间比用户区大的作业
覆盖技术
早期计算机在一定的条件下,可以采用所谓的“覆盖”技术,使大作业在小内存上得以运行。
如:
有一个用户作业程序的调用结构。主程序MAIN需要存储量10KB。运行中,它要调用程序A或B,它们各需要存储量50KB和30KB。程序A在运行中要调用程序C,它需要的存储量是30KB。程序B在运行中要调用程序D或程序E,它们各需要存储量20KB或40KB。通过连接装配的处理,该作业将形成一个需要存储量180KB的相对地址空间,。这表明,只有系统分配给它180KB的绝对地址空间时,它才能够全部装入并运行。
该程序中的子程序A和B不可能同时调用,即MAIN调用程序A,就肯定不会调用程序 B,反之亦然。同样地,子程序 C、D和 E也不可能同时出现,所以,除了主程序必须占用内存中的10KB外,A和B可以共用一个存储量为50KB的存储区,C、D和E可以共用一个存储量为40KB的存储区。也就是说,只要分给该程序100KB的存储量,它就能够运行。由于A和B共用一个50KB的存储区,C、D和E共用一个40KB的存储区,我们就称50KB的存储区和40KB的存储区为覆盖区。因此,所谓“覆盖”是早期为程序设计人员提供的一种扩充内存的技术,其中心思想是允许一个作业的若干个程序段使用同一个存储区,被共用的存储区被称为“覆盖区”。不过,这种技术并不能彻底解决大作业与小内存的矛盾。
对换技术
为了让单一连续分区存储管理能具有“多道”的效果,在一定条件下,可以采用所谓的“对换”技术来实现。“对换”的中心思想是:将作业信息都存放在辅助存储器上,根据单一连续分区存储管理的分配策略,每次只让其中的一个进入内存投入运行。当运行中提出输入/输出请求或分配给的时间片用完时,就把这个程序从内存储器“换出”到辅助存储器,把辅助存储器里的另一个作业“换入”内存储器运行。
这样,从宏观上看,系统中就同时有几个作业处在运行之中。不过要注意,因为单一连续分区存储管理实行的是静态重定位,所以,“换出”的作业程序再被“换入”时,仍应该装到与它“换出”前相同的存储区中去,以保证能够正确地继续运行。不难看出,“对换”是以辅助存储器作为内存的后援而得以实行的,没有它的支持,就谈不上“对换”。
固定分区存储管理
预先把内存储器中可供分配的用户区划分成若干个连续的分区,每个分区的尺寸可以相同,也可以不同。划分后,内存储器中分区的个数以及每个分区的尺寸保持不变。每个分区中只允许装入一个作业运行。
对作业的组织
由于分区尺寸在划分后保持不变,因此系统可以为每一个分区设置一个后备作业队列,形成多队列的管理方式,如图(a)所示。
在这种组织方式下,一个作业到达时,总是进入到“能容纳该作业的最小分区”的那个后备作业队列中去排队。
把到达的作业根据上述原则排成若干个后备队列时,可能会产生有的分区队列忙碌、有的分区队列闲置的情形。
作为一种改进,可以采用对多个分区只设置一个后备作业队列的办法,如图(b)所示。当某个分区空闲时,到这一个队列里去挑选作业,装入运行。
分区的分配与释放
如果采用的是多个队列的管理方式,那么任何一个分区空闲时,只要关于它的队列非空,就把该分区分配给队列的第一个作业使用;一旦作业运行完毕,就收回该分区,进行下一次分配。这时,分区的分配和释放是很容易完成的事情。
如果采用的是一个队列的管理方式,那么在任何一个分区被释放时,就要根据某种方案从该队列中挑选出一个作业装入运行。可以有如下的几种挑选方案。
- 在队列中挑选出第一个可容纳的作业进入。优点:实现简单、选择效率高 缺点:小作业进入大空间会浪费存储空间,存储利用率不高
- 在整个队列中进行搜索,找到这个分区能够容纳的最大的那个作业,让它进入运行。 优点:存储空间的利用率高 缺点:选择效率低下,小作业的优先级低
- 在系统中至少保留一个小的分区,以避免因为运行小作业而被迫分配大分区的情形发生。
为了具体管理内存中的各个分区,操作系统的做法是设置一个“分区分配表”,用它记录各分区的信息以及当前的使用情况。
分区分配表中至少应该有每个分区的起始地址和长度,并且有一个使用标志。当某分区的使用标志为“0”时,表示该分区当前是空闲的,可以分配;当某分区的使用标志不为“0”时,表示该分区已经分配给一个作业使用,该标志里存放的即是这个作业的名称。
当需要把一个作业装入内存时,按照分区号扫视分区分配表,找到使用标志为“0”的分区,随后把要装入内存的作业尺寸与该分区的长度进行比较。若能够容纳该作业,并符合所采取的分配策略,就把它分配给这个作业,同时修改分区分配表中该分区表目的使用标志为非0(即把该作业的名字填入),从而完成分区的分配工作;当一个作业运行结束时,只需根据作业名,在分区分配表里找到它所使用的表目,将该表目的使用标志改为“0”,从而完成该分区的释放工作。
地址重定位与存储保护
在固定分区存储管理中,每一个分区只允许装入一个作业,作业在运行期间没有必要移动自己的位置,因此,在采用这种存储管理方式时,应该对程序实行静态重定位。即决定将某一个分区分配给一个作业时,重定位装入程序就把该作业程序指令中的相对地址与该分区的起始地址相加,得到相应的绝对地址,用该绝对地址修改程序指令中的相对地址,实现对指令地址的重定位,最终完成程序的装入。
在固定分区存储管理中,不仅要防止用户程序对操作系统的侵扰,也要防止用户程序对用户程序的侵扰。因此必须在CPU中设置一对专用的寄存器,用于存储保护
当进程调度程序调度某个作业进程运行时,就把该作业所在分区的低边界地址装入低界限寄存器,把高边界地址装入高界限寄存器。
固定分区存储管理的特点:
- 简单、具有“多道”色彩的存储管理方案。对比单一连续分区,提高了内存资源的利用率。
- 当把一个分区分配给某个作业时,该作业的程序将一次性地全部被装入到分配给它的连续分区里
- 对进入分区的作业程序,实行的是静态重定位。在分区内的程序不能随意移动,否则运行就会出错
缺点:
- 进入分区的作业尺寸,不见得与分区的长度相吻合,势必产生内部碎片,引起内存资源的浪费。
- 如果到达作业的尺寸比任何一个分区的长度都大,它就无法运行。
可变分区存储管理
可变分区存储管理的基本思想
在作业要求装入内存储器时,如果当时内存储器中有足够的存储空间满足该作业的需求,就划分出一个与作业相对地址空间同样大小的分区,并分配给它。
区存储管理中的“可变”也有两层含义,一层是分区的数目随着进入作业的多少可变,另一层是分区的边界划分随作业的需求可变。
可变分区存储管理时,分区的划分是按照进入作业的尺寸进行的,所以分区中不会出现内部碎片。避免了存储浪费。
可变分区存储管理的工作过程中,需要解决的一些技术问题。
假定有作业请求序列:
作业A需要存储16KB,作业B需要存储100KB,作业C需要存储70KB,作业D需要存储75KB等。
内存储器共256KB,操作系统占用20KB,系统最初有空闲区236KB
下面着重讨论236KB空闲区的变化。
作业A到达后,按照它的存储要求,划分一个16KB 的分区,并分配给它,于是出现两个分区,一个已经分配,另一个为空闲,如图(b)所示。
作业B到达后,按照它的存储要求,划分一个100KB的分区,并分配给它,于是出现3个分区,两个已经分配,一个为空闲,如图c)所示。
紧接着为作业C划分一个分区,从而形成4个分区,3个已经分配,一个空闲,如图(d)所示。
当作业D到达时,由于系统内只有50KB的空闲区,不够D的需求,因此作业D暂时无法进入。
如果这时作业B运行完毕,释放它所占用的100KB存储量,这时系统中虽然仍保持为4个分区,但有的分区的性质已经改变,成为两个已分配,两个空闲,如图(e)所示。
由于作业B释放的分区有100KB,可以满足作业D的需要,因此系统在36KB~136KB的空闲区中划分出一个75KB的分区,给作业D使用。
这样36KB~136KB分区被分为两个分区,一个分配出去(36KB~111KB),一个仍为空闲(111KB~136KB),如图(f)所示。
这样,总共有5个分区:3个已经分配,两个空闲。
随着作业对存储区域的不断申请与释放,发展趋势是:系统中所划分的分区数目在逐渐增加,每个分区的尺寸在逐渐减小。
导致的后果是:空闲分区能够满足作业存储要求的可能性在下降。如果这样发展下去,每一个分区的尺寸会越来越小,甚至有可能分配不出去。在存储管理中,把那些无法满足作业存储请求的空闲区称为“外部碎片”。内部碎片是分配给了用户而用户未用的存储区;外部碎片是无法分配给用户使用的存储区。
可以这样理解:一匹布卖给用户,用户做完衣服后剩下的就是“内部碎片”;而卖到最后,这匹布剩下的零头已无人再买,就是“外部碎片”。
假如现在又达到一个作业E,存储要求是55KB。现在有两个空闲分区,一个为25KB,另一个为50KB,可是这两个分区谁也不能满足作业E提出的存储要求,但它们的和75KB比55KB大。很容易想到的办法是将两个空闲区进行合并,可是要合并这两个空闲区,势必要移动作业 C。试想,如果在可变分区存储管理中实行的是静态重定位,就不能随便移动作业,因为其程序指令中的地址已经与所在的绝对地址空间紧密地联系在了一起。这是可变分区存储管理所引起的分区合并,以及相应的地址重定位问题。
又比如说,现在到达的作业E的存储要求是20KB,而不是55KB。这时从图3-11(f)看到,现有的两个分区都可以满足作业E的要求。下面分析到底把111KB~136KB的分区分配给作业E,还是把206KB~256KB的分区分配给作业E。如果把111KB~136KB的分区分配给作业E,它就被分成两个分区:111KB~131KB的已分配区和131KB~136KB的空闲区。剩下的这个空闲区只有5KB大小,将来可以满足作业存储要求的可能性小了。如果把206KB~256KB的分区分配给作业E,它就被分成两个分区:206KB~226KB的已分配区和226KB~256KB 的空闲区。把大的空闲区划分后,将来有大作业到达时,就难以满足它的存储要求。这是可变分区存储管理所引起的分区分配算法问题。
要实施可变分区存储管理,必须解决如下3个问题:
- 采用一种新的地址重定位技术,以便程序能够在内存储器中随意移动,为空闲区的合并提供保证,那就是动态地址重定位。
- 记住系统中各个分区的使用情况,谁是已经分配出去的,谁是空闲可分配的。当一个分区被释放时,要能够判定它的前、后分区是否为空闲区。若是空闲区,就进行合并,形成一个大的空闲区。
- 给出分区分配算法,以便在有多个空闲区都能满足作业提出的存储请求时,能决定分配给它哪个分区。
地址动态重定位的过程
等到真正执行某一条指令时,再根据当前程序所在的区域,对指令中的地址进行重定位。由于这种方式的地址转换是在程序执行时动态完成的,故称为地址的“动态重定位”。
为了实施地址的动态重定位,硬件中要增加一个地址转换机构,负责完成该任务,这个机构一般由地址转换线路和一个“定位寄存器”(也称“基址寄存器”)组成。每当用户程序运行时,把它所在分区的起始地址置入定位寄存器中。CPU 每执行一条指令,就把指令中的相对地址与定位寄存器中的值相加,得到所对应的绝对地址。然后按照这个绝对地址去执行该指令,访问所需要的存储位置。
上图是对动态重定位过程的一个描述
为了对图(a)中的用户作业 A实行可变分区存储管理,假定按照当前内存储器的分配情况,把它原封不动地装入到22KB~25KB的分区里面。可以看到,在其绝对地址空间里,位于22KB+3000单元处的指令仍然是“call 100”,未对它做任何的修改。如果现在调度到该作业运行,操作系统就把它所占用的分区的起始地址22KB装入到定位寄存器中,如图(b)所示。当执行到位于单元22KB+3000中的指令“call 100”时,硬件的地址变换线路就把该指令中的地址100取出来,与定位寄存器中的22KB相加,形成绝对地址22628(=22KB+100)。按照这个地址去执行call指令。于是,程序就正确转移到22628的子程序处去执行了。
静态重定位和动态重定位做下列综合性的比较:
- 地址转换时刻:静态重定位是在程序运行之前完成地址转换的;动态重定位却是将地址转换的时刻推迟到指令执行时进行。
- 谁来完成任务:静态重定位是由软件完成地址转换工作的;动态重定位则由一套硬件提供的地址转换机构来完成。
- 完成的形式:静态重定位是在装入时一次性集中地把程序指令中所有要转换的地址加以转换;动态重定位则是每执行一条指令时,就对其地址加以转换。
- 完成的结果:实行静态重定位,原来的指令地址部分被修改了;实行动态重定位,只是按照所形成的地址去执行这条指令,并不对指令本身做任何修改。
空闲区的合并
在可变分区存储管理中实行地址的动态重定位后,用户程序就不会被“钉死”在分配给自己的存储分区中。必要时它可以在内存中移动,为空闲区的合并带来了便利。
内存区域中的一个分区被释放时,与它前后相邻接的分区可能会有4种关系出现,如图所示。在图中,我们做这样的约定:位于一个分区上面的分区,称为它的“前邻接”分区,位于一个分区下面的分区,称为它的“后邻接”分区。
- (a)表示释放区的前邻接分区和后邻接分区都是已分配区,因此没有合并的问题存在。此时的释放区自己形成一个新的空闲区,该空闲区的起始地址就是该释放区的起始地址,长度就是该释放区的长度。
- (b)表示释放区的前邻接分区是一个空闲区,后邻接分区是一个已分配区,因此,释放区应该和前邻接的空闲区合并,成为一个新的空闲区。这个新空闲区的起始地址是原前邻接空闲区的起始地址,长度是这两个分区的长度之和。
- (c)表示释放区的前邻接分区是一个已分配区,后邻接分区是一个空闲区,因此,释放区应该和后邻接的空闲区合并,成为一个新的空闲区。这个新空闲区的起始地址是该释放区的起始地址,长度是这两个分区的长度之和。
- (d)表示释放区的前邻接分区和后邻接分区都是一个空闲区,因此,释放区应该和前、后两个邻接的空闲区合并,成为一个新的空闲区。这个新空闲区的起始地址是原前邻接空闲区的起始地址,长度是这3个分区的长度之和。
空闲分区的合并,有时也被称为“存储紧凑”。 何时进行合并,操作系统可以有两种时机的选择方案: 一是调度到某个作业时,当时系统中的每一个空闲分区尺寸都比它所需要的存储量小,但空闲区的总存储量却大于它的存储请求,于是就进行空闲存储分区的合并,以便能够得到一个大的空闲分区,满足该作业的存储需要。 二是只要有作业运行完毕归还它所占用的存储分区,系统就进行空闲分区的合并。 比较这两种方案可以看出,前者要花费较多的精力去管理空闲区,但空闲区合并的频率低,系统在合并上的开销小;后者总是在系统里保持一个大的空闲分区,因此对空闲分区谈不上更多的管理,但是空闲区合并的频率高,系统在这上面的开销大。
分区的管理与组织方式
采用可变分区方式管理内存储器时,内存中有两类性质的分区:
一类是已经分配给用户使用的“已分配区”,另一类是可以分配给用户使用的“空闲区”。
随着时间的推移,它们的数目都在不断地变化。如何知道哪个分区是已分配的,哪个分区是空闲的?如何知道各个分区的尺寸是多少?这就是分区管理所要解决的问题。
常用的方式有3种:表格法、单链表法和双链表法
表格法
为了记录内存中现有的分区以及各分区的类型,操作系统设置两张表格,一张为“已分配表”,一张为“空闲区表”
表格中的“序号”是表项的顺序号,“起始地址”、“尺寸”和“状态”都是该分区的相应属性。由于系统中分区的数目是变化的,因此每张表格中表目的项数要足够多,暂时不用的表项的状态被设为“空”。
(a)为当前内存中的分区使用情况,(b)记录了已分配区的情形(c)记录了空闲区的情形
当有作业进入提出存储需求时,就去查空闲区表里状态为“空闲”的表项。如果该项的尺寸能满足所求,就将它一分为二:分配出去的那一部分在已分配表中找一个状态为“空”的表项进行登记,剩下的部分(如果有的话)仍在空闲区表中占据一个表项。如果有一个作业运行结束,则根据作业名到已分配表中找到它的表项,将该项的“状态”改为“空”,随即在空闲区表中寻找一个状态为“空”的表项,把释放分区的信息填入,并将该表项的状态改为“空闲”。这时可能还会进行空闲区的合并工作。
例:现在到达一个作业 E,存储请求是30KB。
- 先查空闲区表。可以看出有2和3号空闲区满足作业E的存储需求30KB。
- 使用起始地址为60KB的空闲分区,该分区一分为二60KB
89KB被分配出去,变成一个已分配区,90KB92KB剩下来,形成一个新的空闲区 - 为了把已分配区60KB~89KB的信息填入已分配表,应该在该表中寻找一个原来状态为“空”的表项
- 由于原来的空闲区还剩下2KB,可以利用原来在空闲表中的表项来反映它的新信息
因为2KB太小了,很难在满足其他作业提出的存储请求。
如果在分配一个空闲区后,它所剩下的空闲分区的长度太小的话,还不如干脆把它一起分配出去为好。
单链表法
把内存储器中的每个空闲分区视为一个整体,在它的里面开辟出两个单元,一个存放该分区的长度(size),另一个存放它下一个空闲分区的起始地址(next)
操作系统开辟一个单元,存放第1个空闲分区的起始地址,这个单元被称为“链首指针”。最后一个空闲分区的next中存放标志“NULL”,表明它是最后一个。
这样系统中的所有空闲分区被连接成一个链表。从链首指针出发,顺着各空闲分区的next往下走,就能到达每一个空闲分区。
用空闲区链表管理空闲区时,对于提出的任何一个请求,都顺着空闲区链表首指针开始查看一个个空闲区。如果第1个分区不能满足要求,就通过它的next找到第2个空闲区。如果一个空闲区的next是NULL,就表示系统暂时无法满足该作业这一次所提出的存储请求。在用这种方式管理存储器时,无论分配存储分区还是释放存储分区,都要涉及 next(指针)的调整。
双链表法
当一个已分配区被释放时,有可能和与它相邻接的分区进行合并。为了寻找释放区前、后的空闲区,以利于判别它们是否与释放区直接邻接,可以把空闲区的单链表改为双向链表。
每个空闲分区中,除了存放下一个空闲区起始地址next,还存放它的上一个空闲区起始地址(prior)的信息
这样,通过空闲区的双向链表,就可以方便地由next找到一个空闲区的下一个空闲区,也可以由prior找到一个空闲区的上一个空闲区。
在把一个释放区链入空闲区双向链表时,通过它的prior发现,在该链表中释放区的前面一个空闲区的起始地址加上长度,正好等于释放区的起始地址,即它前面的空闲区与它直接相邻接,应该把这个释放区与原来的空闲区合并。
释放区起始地址加上长度正好等于next所指的下一个空闲区的起始地址,即它后面的空闲区与它直接相邻接,应该把这个释放区与原来的空闲区合并。
地址法:当有一个释放区要进入链表时,依据它的起始地址,找到它在链表中的正确位置,然后调整指针进行插入。
尺寸法:当有一个释放区要进入链表时,要依据它的尺寸,在链表中找到它的合适位置,调整指针进行插入。
空闲分区的分配算法
在可变分区存储管理中,常用的分区分配算法有:最先适应算法、最佳适应算法以及最坏适应算法。 最先适应算法 把最先找到的、满足存储需求的那个空闲分区作为分配的对象。 尽量减少查找时间,它实现简单,但有可能把大的空闲分区分割成许多小的分区,因此对大作业不利。 最佳适应算法 从当前所有空闲区找出一个能够满足存储需求的、最小的空闲分区作为分配的对象。 尽可能不把大的空闲区分割成小的分区,以保证大作业的需要。 算法实现比较费时间、麻烦。 最坏适应算法 总是从当前所有空闲区中找出一个能够满足存储需求的、最大的空闲分区作为分配的对象。 照顾中、小作业的需求
图(b)最先适应算法分配作业D、最坏适应算法分配作业D
图(c)最佳适应算法分配作业D
可变分区存储管理特点:
- 作业一次性全部装入到一个连续的存储分区中
- 分区是按照作业对存储的需求划分的,所以在可变分区存储管理中,不会出现内部碎片 避免了存储浪费
- 为保证作业能在内存中移动,需要由硬件支持,实行指令地址的动态重定位
可变分区存储管理缺点:
- 没有解决小内存运行大作业的问题,如果作业的存储需求大于系统提供的整个用户区,就无法投入运行
- 避免了内部碎片造成的存储浪费,但是可能出现极小的分区暂时分配不出去的情况,引起外部碎片
- 为了大的分区,可变分区存储管理通过移动程序来达到分区合并的目的,移动很花费时间,增加了系统在这方面的投入
伙伴系统
固定分区限制了系统中可运行程序的道数,分区中产生的内部碎片,使内存空间的利用率较低。可变分区虽然消除了内部碎片,但维护起来很复杂,并且需要为存储紧缩付出额外的开销。 伙伴系统是对固定分区和可变分区两种存储管理“扬长避短”后,提出的一种折中方案。 在伙伴系统中,可用内存分区的大小为2K,L≤K≤U,其中:
- 2L表示分配的最小分区的尺寸;
- 2U表示分配的最大分区的尺寸。
用于分配的整个内存空间为一个大小为2U的分区
若所需存储分区的大小 s满足2U-1<s≤2U,则将整个存储空间分配出去;
否则,将该分区分成尺寸均为2U-1的伙伴。
如果有2U-2<s≤2U-1,则为该存储请求分配两个伙伴中的任一个;
否则,其中的一个伙伴又被分成两半。
这样的过程一直继续下去,直到获得大于或等于s的最小分区,并完成分配。
有一个1MB内存,采用伙伴系统管理存储空间的分配和回收。系统的运行轨迹为:作业A请求100KB的存储;作业B请求240KB的存储;作业C请求64KB的存储;作业D请求256KB的存储;随后作业B和作业A先后完成;作业E请求75KB的存储;最后作业C、E、D完成。
分页式存储管理
分页式存储管理是将固定式分区方法与动态重定位技术结合在一起,提出的一种存储管理方案,它需要硬件的支持。分页式存储管理的基本思想是:首先把整个内存储器划分成大小相等的许多分区,每个分区称为一“块”(这表明它具有固定分区的管理思想,只是这里的分区是定长罢了)。在分页式存储管理中,块是存储分配的单位。比如把内存储器划分成n个分区,编号为0,1,2,…,n−1。例如在图(a)中,内存储器总的容量为256KB,操作系统要求20KB。若块的尺寸为4KB,则共有64块,操作系统占用前5块,其他分配给用户使用。
用户作业仍然相当于“0”进行编址,形成一个连续的相对地址空间
操作系统接收用户的相对地址空间,然后按照内存块的尺寸对该空间进行划分
用户程序相对地址空间中的每一个分区被称为“页”,编号从0开始,第0页、第1页、第2页……例如在图(b)中,作业A的相对地址空间大小为11KB。按照每页4KB来划分,它有2页多不到3页大小,于是就把它作为3页来对待,编号为第0页、第1页和第2页。
这样,用户相对地址空间中的每一个相对地址,都可以用“页号,页内位移”的数对表示。
数对(页号,页内位移)与相对地址是一一对应的
相对地址5188与数对(1,1092)相对应,其中“1”是相对地址所在页的页号,“1092”则是相对地址与所在页起始位置(4KB =4096)之间的位移
相对地址9200与数对(2,1008)相对应,“2”是相对地址所在页的页号,“1008”是相对地址与所在页起始位置(8KB = 8192)之间的位移。
有了这些准备,如果能够解决作业原封不动地进入不连续存储块后也能正常运行的问题,那么分配存储块是很容易的事情,只要内存中有足够多的空闲块,作业中的某一页进入哪一块都是可以的。例如在图(a)中,就把作业A装入了第8块、第11块和第6块这样3个不连续的存储块中。
例:
假定块的尺寸为1KB,作业A的相对地址空间为3KB,在相对地址100处有一条调用子程序的指令call,子程序的入口地址为3000(当然是相对地址)。作业A进入系统后被划分成3页,如图(a)所示。现在把第0、1、2页依次装入内存储器的第4、9、7这3个不连续的块中,如图(b)所示。
为了保证原封不动放在不连续块中的用户作业A能够正常运行,可使用以下方法:
- 记录作业A的页、块对应关系,如图(c)
- 当运行到指令“call 3000”时,把相对地址3000转换成数对(2,952),表示该地址在作业相对地址空间里第2页,起始位置的位移是952.
页号=相对地址/块尺寸(这里的“/”运算符表示整除) 页内位移=相对地址%块尺寸(这里的“%”运算符表示求余)
- 用数对中的“页号”查作业A的页、块对应关系表,得到相对地址空间中的第2页内容,现在是在内存的第7块中
- 把内存第7块的起始地址与页内位移相加,就得到了相对地址3000现在的绝对地址。7KB+952=8120
系统执行指令 call 8120 就获得了正确的执行
指令中相对地址的重定位工作,是在指令执行时进行,属于动态重定位,并且由以下内容一起来实现地址的动态重定位:
- 将相对地址转换成数对(页号,页内位移)
- 建立一张作业的页、块对应表
- 按页号去查页、块对应表
- 由块的起始地址与页内位移形成绝对地址
分页式存储管理的地址转换
1.地址结构与数对(页号,页内位移)的形成
在分页式存储管理第地址变换中,系统要把指令中的相对地址转换成它所对应的数对,并且给出进行这种变换时使用的计算公式。
每次地址变换都要做这种映射计算,不仅费时,而且麻烦。
若把块(或页)的尺寸限定只能是2的方幂,那么利用计算机系统设定的地址结构,就很容易得到一个相对地址所对应的数对,根本不用进行上面的复杂计算。
例:
某系统的地址结构长为16个二进制位,该地址空间有64KB(2的16次方)大,即0~65535。相对地址3000的二进制表示。我们假定系统的块尺寸为1KB=1024Byte。
3000对应的数对是“2,952”。其实分析一下就可以知道,现在一页的尺寸为1KB,表明每一页中有1024个字节,它需要用10个二进制位来表示。因此,我们在第10个二进制位处画一条粗的竖线,如图(b)所示。
第0位到第9位这10个二进制位就可以表示一页中0~1023这1024个字节。如果超过1024个字节就进位。于是,前面的第10位到第15位就表示第几页。
图(b)中高6位的二进制数值是2,正是3000对应的页号,低10位的二进制数值是952,正是3000对应的页内位移。
如果现在把一页的尺寸设置为256字节。
因为2的8次方等于256 使用用0
7位标识页内位移,815标识页号
如图(c)计算高8位(8~15)相应的二进制数值是11;计算低8位(0~7)相应的二进制数值是184。因此,当每页长度为256字节时,相对地址3000应该在第11页的第184处。11*256=2816,2816+184=3000
例:
一台计算机的内存总存储量为65536字节,块的尺寸为4096字节。现在有一个用户程序,其代码段长为32768字节,数据段长为16386字节,栈段长为15870字节。试问这个用户程序能适合该机器的内存空间吗?如果把一块改为512字节大小呢?(注意:不允许一块里包含两个段的内容,即不允许里面既有代码段的内容,又有栈段的内容。)
65536字节的存储空间,总共含16个长度为4096字节的块。用户作业程序的代码段长32768字节,需要8块;数据段长16386字节,需要5块;栈段长15870字节,需要4块。由于不允许一块中包含两个段的内容,该作业总共需要内存空间17块。这就是说,当每块为4096字节时,该作业无法装入内存。
若将块尺寸改为512字节,则整个存储空间被划分成128块。此时,代码段需要64块,数据段需要33块,栈段需要31块,共需要128块。于是,此时的存储空间划分能够适应该作业的存储需求。
例:
在分页式存储管理中,建立了某个作业的页、块对应关系为:第0页放在第0块,第1页放在第3块,第2页放在第1块,如下表所示。已知块的尺寸为1KB/块。试用公式求相对地址1023、1024、3000所对应的绝对地址。
块尺寸大小为1024(1KB),所以第1块起始地址是1024,第3块起始地址是3072。
首先求它们所对应的数对
1023所对应的数对是:
1023/1024=0
1023%1024=1023
(0,1023)
用0去查对应关系表,可知第0页放在第0块。故用第0块的起始地址与页内位移相加,得到绝对地址为1023。
1024所对应的数对是:
1024/1024 = 1
1024%1024 = 0
(1,0)
用1去查对应关系表,可知第1页放在第3块。故用第3块的起始地址与页内位移相加,得到绝对地址为3072。
3000所对应的数对是:
3000/1024 = 2
3000%1024 = 952
(2,952)
用2去查对应关系表,可知第2页放在第1块。故用第1块的起始地址与页内位移相加,得到绝对地址为1976。
2.页表与快表
系统为了知道一个作业的某一页存放在内存中的哪一块中,就需要建立起它的页、块对应关系表。在操作系统中,把这张表称为“页表”。
因此,在分页式存储管理中,每一个作业都有自己的页表。用户作业相对地址空间划分成多少页,其页表就有多少个表项,表项按页号顺序排列。
页表的构成一种方法是利用内存储器。
只需要硬件设置一个专用寄存器--“页表控制寄存器”
每当进程调度时,把调度到作业的页表起始地址和页长度放入寄存器中,就能达到映射不同作业地址的目的。
地址转换过程:
- 先把CPU指令中的相对地址分解成数对(页号,页内位移)
- 接着由页号与页表起始地址得到该页号在页表中对应的表目,从而查到相应的块号,再由块号与页内位移拼出绝对地址
- 此时才去执行所需要的指令
页表控制寄存器中的“长度”起到存储保护的作用,每一个相对地址中的页号都不能大于该长度,否则出错。
页表存放在内存,不仅增加了系统在存储上的花销,更重要的是还降低了 CPU 的访问速度。因为每次对某一地址访问,首先要访问内存中的页表,形成绝对地址后,才能进行所需要的真正访问。也就是说,以前只需一次访问就能实现的操作,现在要两次访问内存储器才能实现。
为了提高地址的转换速度,另一种实现页表的方法是用一组快速的硬件寄存器构成公用的页表。
调度到谁时,就把谁在内存的页表内容装入该组寄存器中。
工作过程:
硬件把页号与寄存器组中所有的表项同时并行比较,立即输出与该页号匹配的块号。这种做法无需访问内存,并且通过并行匹配直接完成地址的变换,速度是极快的。
由于快速寄存器价格昂贵,完全由它来组成页表的方案是不可取的。
考虑到大多数程序在一次调度运行时,倾向于在少数页面中进行频繁的访问(这被称为程序的“局部性”原理),因此实际系统中的做法是采用内存页表与快速寄存器组相结合的解决方案,并且利用程序的局部性原理,只用极少数几个(一般是8~16个)快速寄存器来构成快速寄存器组,这时把快速寄存器组单独起名为“相联寄存器”,或简称“快表”。
快表中存放的是页表内容的一部分。在得到一个相对地址并划分出数对(页号,页内位移)后,系统总是先通过页号与快表中的所有表项并行比较。如果发现了匹配的页,则将块号直接从快表中取出,而不必通过页表。于是该块号与页内位移拼接,形成所需要的绝对地址。只有当快表中没有匹配的页号时,地址转换机构一方面按照普通的访问页表的方式工作,以获得所需要的绝对地址;另一方面,把这个页号与块号的对应关系送入快表保存,以便下一次进行地址转换时能够命中。如果快表里没有空的表项,则要先删除快表中的一个表项,然后将新的页表表项替换进去。
通过查快表就能实现内存访问的成功率为“命中率”。可见,命中率越高,性能就越好。
例:
假定CPU访问一次内存的时间为200ns,访问一次快表的时间为40ns。如果快表的命中率为90%。试问现在进行一次内存存取的平均时间是多少?比只采用页表下降了多少?
通过快表进行内存存取的时间是200 + 40 = 240ns;
通过页表进行内存存取的时间是200 + 200 = 400ns。
由于快表的命中率为90%,因此现在进行一次内存存取的平均时间是:
(200 + 40) × 90% + (200 + 200) × 10% = 240 × 90% + 400 ×10% = 256ns
不采用快表,只用页表进行内存存取,每次需要400ns。采用快表比只采用页表少花400 − 256 = 144ns
144ns在400ns中所占的比率为:(144/400) × 100% = 36%,即下降了36%
例:
假定访问页表的时间为100ns,访问相联存储器的时间为20ns。希望把进行一次内存访问的平均时间控制在140ns内。试问这时要求相联存储器的命中率为多少?
访问页表的时间为100ns,也就是说访问一次内存需要100ns。
于是通过页表进行一次内存存取的时间为100+100,通过快表进行一次内存存取的时间是100+20。设命中率为x,于是有等式如下:
(100 + 100) × (1 − x) + (100 + 20) x = 140
求解后可以得到x = 75%,即命中率至少应该是75%。
页面尺寸也需要考虑的问题。任何一个作业的长度不可能总是页面尺寸的整数倍,因此平均来说,分配给作业的所有内存块的最后一块里会有一半是浪费掉的,它成为了内部碎片。
从这个角度出发,应该把页面尺寸定得小一些。但是,页面尺寸小了,用户作业相对地址空间划分的页面数势必增加,每个作业的页表就会随之加大。
由上面的分析可以看出,选择最佳的页面尺寸,可能需要在几个相互矛盾的因素之间平衡求得。通常,页面尺寸大多选在512Byte到64KB之间。
内存块的分配与回收
分页式存储管理是以块为单位进行存储分配的,并且每块的尺寸相同。
在有存储请求时,只要系统中有足够的空闲块存在,就可以进行存储分配,把谁分配出去都一样,没有好坏之分。为了记住内存块里谁是已分配的,谁是空闲的,可以采用“存储分块表”、“位图”以及“单链表”等来管理内存中的块。
“存储分块表”,就是操作系统维持一张表格,它的一个表项与内存中的一块相对应,用来记录该块的使用情况。
如图(a)所示,内存总的容量是64KB,每块4KB,于是被划分成16块。这样,相应的存储分块表也有16个表项,它恰好记录了每一块当前的使用情况,如图(b)所示。
当有存储请求时,就查存储分块表。只要表中记录的“空闲块总数”大于请求的存储量就可以进行分配,同时把表中分配出去的块的状态改为“已分配”。当作业完成归还存储块时,就把表中相应块的状态改为“空闲”。
当内存储器很大时,存储分块表就会很大,要花费掉相当多的存储量,于是出现了用位图记录每一块状态的方法。所谓“位图”,即是用二进制位与内存块的使用状态建立起关系,该位为“0”,表示对应的块空闲;该位为“1”,表示对应的块已分配。这些二进制位的整体,就称为“位图”。图(c)所示就是由3个字节组成的位图,前两个字节是真正的位图(共16个二进制位),第三个字节用来记录当前的空闲块数。
分配过程:
- 首先查看当前空闲块数能否满足作业提出的存储需求
- 若不能满足,则该作业不能装入内存
- 若能满足,一方面根据需求的块数,在位图中找出一个个当前取值为“0”的位,把它们改为取值“1”,修改“空闲块总数”。
- 按照所找到的位的位号以及字节号,按公式(块号 = 字节号 × 8 + 位号)计算出该位所对应的块号
- 把作业相对地址空间里的页面装入这些块,并在页表里记录页号与块号的这些对应关系,形成作业的页表。
- 在作业完成运行,归还存储区时,按照公式(字节号 = 块号/ 8;位号 = 块号%8),根据归还的块号计算出该块在位图中对应的是哪个字节的哪一位,把该位置成“0”,实现块的回收。
在这里也可以把空闲块链接成一个单链表加以管理,如图(a)。系统必须设置一个链表的起始地址指针,以便进行存储分配时能够找到空闲的内存块。 分页式存储管理的特点:
- 内存储器事先被划分成相等尺寸的块(进行存储分配的单位)
- 用户作业的相对地址空间按照块的尺寸划分成页(这种划分在系统内部进行)
- 实行动态重定位,打破作业必须占据连续存储空间的限制,作业在不连续的存储区里也能正确运行
分页式存储管理的缺点:
- 平均每一个作业要浪费半页的存储块,产生内部碎片
- 作业虽然可以不占据连续的存储区,但每次仍然要求一次全部进入内存。如果作业较大,其存储需求大于内存,不能运行。
分段式存储管理
分段式存储管理的引入,是为了方便用户编程、存储共享和存储保护
分段及二维逻辑地址空间
之前用户编写程序时,是一段一段程序地编写,对它们分别编译后,再链接编辑成为一个统一的、相对于“0”编址的线性地址空间。用户向系统提供的是这样一个一维地址空间 这种组织方式的缺点:
- 在统一的一维地址空间中,程序段在内存一个接着一个紧紧地放在一起,中间没有空隙。结果是改变一个程序段的大小,就会影响其他无关程序段的起始地址;对某个程序段进行修改,就会影响到其他相关部分,就可能要对所有的程序重新进行链接编辑。
- 在统一的一维地址空间中,由于不清楚某程序段在内存空间的具体位置,因此不利于按照程序段的性质,实施存储保护和共享。
如果不把各程序段链接成一个统一的地址空间,而是按照程序的性质、以一个个独立地址空间的形式提交给系统,情况就会改变
- 每个程序段都是一个独立的地址空间,它们可以独自增长或缩短小而不用顾及别的程序段,不会影响到其他程序段的地址空间
- 每个程序段是一个独立的逻辑实体,因此可以对它们进行不同类型的存储保护,如对过程段指明为只允许执行,禁止读和写;对数据段指明可以读、写,但不允许执行,任何试图转移到该段的执行都将被禁止。
- 独立的地址空间有助于实施对过程和数据的共享,因为可以把需要共享的内容独立成为一段,纳入到不同的用户作业地址空间里,而不必在每一个用户作业的地址空间里都有它的一个备份。
所谓“分段式”存储管理,即是要求用户将自己的整个作业程序以多个相互独立的称为“段”的地址空间提交给系统,每个段都是一个从“0”开始的一维地址空间,长度不一。操作系统按照段长为作业分配内存空间。
段表及地址变换过程
实施分段式存储管理时,系统为每个用户作业设置一个段表,用于记录各段在内存中的存放信息。
逻辑空间中有多少段,段表里就有多少个表项。每个表项通常包括的信息有段号、段长、该段在内存的基址(即起始地址)等。
为了能够完成地址变换,硬件需要提供一个段表基址寄存器。每当重新调度时,就把调度到的作业的段表起始地址及段表长度(即段表中表项的个数)放入寄存器中,从而达到映射不同作业的目的。
分段式存储管理时的地址转换过程,具体步骤如下。
- 从CPU给出的相对地址数对[段号s,段内位移d]中提取出段号s,用来进行地址转换
- 如果段号s大于段表长度,表示该地址越界出错;否则以这个段号为索引查段表,得到该段在内存的基址
- 用相对地址中的段内位移d与该段段长比较,如果大于段长则表示该地址出错;否则,由段的基址和段内位移d拼装出所需的物理地址,从而实现对内存的访问
由于段表放在内存,CPU 每次访问内存中的一个元素,都要对内存进行两次访问,从而使程序的执行速度降低。
例:
在分段式存储管理中,某作业的段表如下
已知该作业中的6个逻辑地址为
[0,430]
[3,400]
[1,10]
[2,2500]
[4, 42]
[1,11]
试求其对应的物理地址。
[0,430] 219+430=649
[3,400] 1327+400=1727
[1,10] 2300+10=2310
[2,2500] 第二段段长为100,段内位越界出错
[4, 42] 1954+42=1996
[1,11] 2300+11=2311
存储保护与共享
1.分页式存储管理中的存储保护与共享 在分页式环境下存储保护只能以页面为单位。在页表的每一个表项里,设置一个所谓的“保护位”,该位的不同取值表示对应的页帧是可读、可写或可执行等。这样,通过检查页的保护位,就可以验证有没有对只读页做了写操作等。 在分页式系统中,用户不知道他的作业被划分了页,不知道他的作业被划分成多少页,不知道每一页的界限在什么地方。 被共享的程序文本部分不一定正好划分在一个或几个完整的页面中,有可能在一个页面中既有允许共享的内容,又有不能共享的私有数据。如果共享该页面,就不利于私有数据的保密;如果不让共享该页面,原来可共享的那部分程序内容就不得不出现在各个进程的内存页中,造成存储空间无谓的浪费。因此,在分页式环境下实现页面的共享比较困难,但也不是不可能。
如有40个用户,每个都要运行一个文本编辑程序。若该文本编辑程序代码段长150KB,数据段长50KB。如果编辑程序不支持共享,就需要8MB的内存来保证40个用户的运行。但如果编辑程序的代码是可重入的(即在执行时绝不会修改自己),就可以对它实施共享。
假定分页式存储管理的页帧尺寸为50KB,那么文本编辑程序被划分成3页,用户进程的数据段被划分成一页,合起来每个用户进程的逻辑地址空间为4页。图画出了3个进程的逻辑地址空间和相应的页表,它们的0~2页都划归给文本编辑程序使用(即ed1,ed2,ed3),页表中的0~2表项都对应于页帧号3、4和6;各进程的数据页(即dataA、dataB、dataC)都位于自己空间的第3页,分别存放在内存的2、8和11页帧。
这样,为了支持40个用户,内存中只需要一个文本编辑程序的复制,加上每个用户运行时所需的数据空间50KB,总共只需要2.15MB存储空间,而不是8MB。
2.分段式存储管理中的存储保护与共享
在分段式环境下,段是有完整意义的逻辑信息单位,因此对段中的所有内容可以采用相同的方式加以使用。
如:程序段只能执行,不能修改;数据段可读可写,但不能执行;如此等等。因此,在分段式存储管理中,很容易对各段实现存储保护和共享。
可以有两种保护:越权保护、越界保护
通过在段表表项里增加权限位,指出每段的访问权限,比如是可读的、可写的或只执行的等。每次进行地址映射时,将该访问的类型与权限位做比较,若不符合就产生出错中断,从而实现越权保护的目的。
在地址变换过程中,需要进行段号和段表长度的比较,以及段内地址和段长的比较。若段号小于段表长度,并且段内地址小于段长,才能去进行地址变换。否则产生越界中断,终止程序的运行,达到越界保护的目的。
在分段式存储管理中,很容易实现段的共享,这只需在有关作业的段表中增加一个表项,让其基址指向共享段在内存中的起始地址。
如进程A和B要对文本编辑程序进行共享,就可以把文本编辑程序作为它们地址空间中的段0,如图所示。假定文本编辑程序存放在内存43062起始的连续分区里,那么在所对应的各段表中,段号为0的表项的基址都是43062,从而达到共享该文本编辑程序的目的。
分段与分页的区别
1.页是信息的物理单位,段是信息的逻辑单位
分页是系统管理的需要,不是用户的需求,用户不知道系统在内部处理时,将自己的空间划分成了很多的页。系统根据内存块的尺寸划分页,并不考虑该页中的信息是否完整。因此,一页对应的是信息的一个物理单位。
分段是基于用户程序结构提出的存储管理模式,用户知道自己的程序分有多少段,每段在逻辑上都是相对完整的一组信息。所以,段是信息的一个逻辑单位。
2.页的尺寸由系统确定,段的尺寸因段而异
分页存储管理的系统,页的尺寸是由系统确定的,与内存页帧的大小相同。
段的长度取决于用户编写的程序,不同的段有不同长度。
3.页式地址空间是一维的,段式地址空间是二维的
在分页式存储管理时,用户必须通过链接编辑程序,把各程序段链接成一个相对于0编址的一维线性空间,程序中是通过地址编号来确定空间中的位置的。因此,用户向系统提供的是一个一维的逻辑地址空间。
在分段式存储管理时,用户不把各程序段连接成一个相对于0编址的一维线性空间,各程序段之间是通过[段号,段内位移]进行访问的。因此,用户向系统提供的是一个二维的逻辑地址空间。
虚拟存储与请求分页式存储管理
虚拟存储器的概念
存储管理方案都要求把作业“一次全部装入”。这就带来了一个很大的问题:如果有一个作业太大,以至于整个内存都容纳不下它,那么,这个作业就无法投入运行。多年来,人们总是受到小内存与大作业之间矛盾的困扰。在多道程序设计时,为了提高系统资源的利用率,要求在内存里存放多个作业程序,这个矛盾就显得更加突出。 要求一次全部装入作业程序,主要就是认为只有作业程序全部在内存里,才能够保证它的正确运行。其实,作业程序运行时,并不一定要全部在内存里。 作业提交给系统时,首先进入辅存。运行时,只将其有关部分信息装入内存,大部分仍保存在辅存中。当运行过程中需要用到不在内存的信息时,再把它们调入,以保证程序的正常运行。 如:有一个1MB 的程序,任何时刻都可以让它的256KB 内容在内存中运行。需要时,就在内存和辅存之间交换所需要的其他程序段信息。于是,1MB程序就可以在256KB的存储器中得以运行。对于用户来说,他只知道程序在内存里才能运行。现在他的1MB程序能够运行,因此他就认为该计算机系统提供的是一个具有1MB的内存。但这只是一个“幻觉”,实际上只提供给他256KB内存。这样一个人们认为有的、但实际上不存在的“大”存储器,就被称为“虚拟存储器”。 虚拟存储器是一种扩大内存容量的设计技术,它把辅助存储器作为计算机内存储器的后援。在虚拟存储意义下,用户作业的相对地址空间,就是系统提供给他的虚拟存储器。在多道程序设计环境下,每一个用户都有自己的虚拟存储器。为了强调和突出虚拟存储,在提供虚拟存储管理的系统里,就把用户作业的相对地址空间改称为“虚拟地址空间”,里面的地址称为“虚拟地址”。 需解决的问题:
- 程序运行时,如何发现需要的信息不在内存。只有能够发现不在,才有可能将其调入内存。
- 不在内存的信息只有进入内存才能被执行。因此,如果需要调入信息时内存没有空余的存储区,应该如何处理。
实现虚拟存储器的技术有基于分页式存储管理的“请求分页式存储管理”、基于分段式存储管理的“请求分段式存储管理”,以及基于段页式存储管理的“请求段页式存储管理”。
请求分页式存储管理的基本思想
请求分页式存储管理是基于分页式存储管理的一种虚拟存储器。
它与分页式存储管理相同的是:先把内存空间划分成尺寸相同、位置固定的块,然后按照内存块的大小,把作业的虚拟地址空间(就是以前的相对地址空间)划分成页(注意这个划分过程对于用户是透明的)。由于页的尺寸与块一样,因此虚拟地址空间中的一页,可以装入到内存中的任何一块中。
它与分页式存储管理不同的是:作业全部进入辅助存储器,运行时,并不把整个作业程序都装入到内存,而只装入目前要用的若干页,其他页仍然保存在辅助存储器里。运行过程中,虚拟地址被转换成数对(页号,页内位移)。
根据页号查页表时,如果该页已经在内存,就有真实的块号与之对应,运行就能够进行下去;如果该页不在内存,就没有具体的块号与之对应,表明为“缺页”,运行就无法继续下去,此时,就要根据该页号把它从辅助存储器里调入内存,以保证程序的运行。
所谓“请求分页式”,即是指当程序运行中需要某一页时,再把它从辅助存储器里调入内存使用的意思。
根据请求分页式存储管理的基本思想可以看出,用户作业的虚拟地址空间可以很大,它不受内存尺寸的约束。
例:
某计算机的内存储器容量为32KB,系统将其划分成32个1KB大小的块。该机的地址结构长度为2的21次方,即整个虚拟存储器最大可以有2MB,是内存的64倍。图(a)给出了虚拟地址的结构,从中可以看出,当每页为1KB时,虚拟存储器最多可以有2048页。这么大的虚拟空间当然无法整个装入内存。图(b)表示将虚拟地址空间放在辅助存储器中,运行时,只把少数几页装入内存块中。
缺页中断的处理
1.页表表项中的“缺页中断位”
请求分页式存储管理中,通过页表表项中的“缺页中断位”来判断所需要的页是否在内存的。
这里的页表表项内容基本由页号、块号、缺页中断位、辅存地址组成
页号:虚拟地址空间中的页号。
块号:该页所占用的内存块号。
缺页中断位:该位为“1”,表示此页已在内存;为“0”,表示该页不在内存。当此位为0时,会发出“缺页”中断信号,以求得系统的处理,将所缺的页从磁盘调入内存。
辅存地址:该页内容在辅助存储器上的存放地址。缺页时,缺页中断处理程序就会根据它的指点,把所需要的页调入内存。
2.请求分页式存储管理运作过程图例
- 内存容量为40KB,被划分为10个存储块,每块4KB,操作系统程序占用第0块。
- 内存第1块为系统数据区,里面存放着操作系统运行时所需要的各种表格
存储分块表:它记录当前系统各块的使用状态,是已分配的,还是空闲的 作业表:它记录着目前进入内存运行的作业的有关数据,例如作业的尺寸、作业的页表在内存的起始地址与长度等信息。 各个作业的页表:每个页表表目简化为3项内容,即页号(P)、块号(B)、以及缺页中断位(R),其实还应该有该页在辅助存储器中的位置等信息。 在图(a)中,画出了作业2的页表在系统数据区里的情形。其实,上面给出的作业表、存储分块表、以及各个作业的页表等都应该在这个区里面,现在把它们单独提出来,形成了图(b)、图(c)及图(d)。
- 这里的地址转换结构仅由页表组成,没有给出相联寄存器(快表)。系统设置了一个页表控制寄存器,在它的里面总是存放着当前运行作业页表的起始地址以及长度。由于现在它的里面放的是作业2的信息,可以看出,当前系统正在运行作业2。
当操作系统决定把CPU分配给作业2使用时,就从作业表中把作业2的页表起始地址(4600)和长度(3)装入页表控制寄存器中。
然后,开始运行作业2.当执行到作业2第0块中的指令“call 8300”时,系统先把它里面的虚拟地址8300转换成数对(2,108)
页号=8300/4096=2 页内位移=8300%4096=108
按照页表控制寄存器的记录,页号2小于寄存器中的长度3,表明虚拟地址8300没有超出作业2所在的虚拟地址空间,因此允许用页号2去查作业2的页表。
现在作业2的第2页不在内存,因为它所对应的页表表目中的R为(缺页中断位)等于0,于是引起“”缺页中断。
系统一方面通过查存储分块表,得到目前内存中第3、7、9块是空闲的。假定现在把第7块分配给作业2的第2页使用。于是把作业2页表的第3个表目中的R改为1, B改为7如图(d)作业2页表第3表项底衬所示
另一方面,根据页表中的记录,获得该页在辅存的位置,并把作业2的第2页调入内存的第7块;再有,系统应该把存储分块表中对应第7块的表目状态由“空闲”改为“已分配”。做完这些事情后,系统结束缺页中断处理,返回到指令“call 8300”处重新执行。
这时,虚拟地址8300仍被转换成数对(2,108),其中
页号= 8300/4096 = 2;页内位移= 8300%4096 = 108
根据页号2去查作业2页表的第3个表目。这时该表目中的R=1,表示该页在内存,且放在了内存的第7块中。这样,用第7块的起始地址28K加页内位移108,形成了虚拟地址8300对应的绝对地址。所以真正应该执行的指令是:“call(28K+108)”。
在页表表项中,如果某项的R位是0,那么它的B位记录的内容是无效的。比如,作业2页表的第2页中,由于它的R原来为0,因此B中的信息“5”是无效的,并不表明现在第2页放在第5块中,它可能是以前留下的痕迹罢了。
3.缺页中断的处理过程
- 根据当前执行指令中的虚拟地址,形成数对(页号,页内位移),用页号去查页表,判断该页是否在存储器中
- 若该页的R位(缺页中段位)为“0”,表示当前该页不在内存,于是产生缺页中断,让操作系统的中断处理程序进行中断处理
- 中断处理程序去查存储分块表,寻找一个空闲的内存块;查页表,得到该页在辅助存储器上的位置,并启动磁盘读信息
- 把从磁盘上读出的信息装入到分配的内存块中
- 根据所分配的存储块的信息,修改页表中相应的表目内容,即将表目中的R位设置成为“1”,表示该页已在内存中,在B位填入所分配的块号。另外,还要修改存储分块表里相应表目的状态
- 由于产生缺页中断的那条指令并没有执行,在完成所需页面的装入工作后,应该返回原指令重新执行。这时所需页面已在内存,因此可以顺利执行下去
4.缺页中断与一般中断的区别
- 缺页中断是在执行一条指令中间时产生的中断,并立即转去处理,一般中断则是在一条指令执行完毕后,当发现有中断请求时,才去响应和处理
- 缺页中断处理完成后,仍返回到原指令去重新执行,因为那条指令并未执行。而一般中断则是到下一条指令去执行,因为上一条指令已经执行完毕了
5.作业运行时的页面走向
作业运行时,程序中涉及的虚拟地址随时在发生变化,它是程序的执行轨迹,是程序的一种动态特征。
由于每一个虚拟地址都与一个数对(页号,页内位移)相对应,因此这种动态特征也可以用程序执行时页号的变化来描述。
通常,称一个程序执行过程中页号的变化序列为“页面走向”。
图里面有3条指令。虚拟地址100中是一条LOAD指令,含义是把虚拟地址1120中的数2000送入1号寄存器;虚拟地址104中是一条ADD指令,含义是把虚拟地址2410中的数1000与1号寄存器中当前的内容(即2000)相加,结果放在1号寄存器中(这时1号寄存器里应该是3000);虚拟地址108中是一条STORE指令,含义是把1号寄存器中的内容存入到虚拟地址1124中。因此,运行结果如图(b)所示,在虚拟地址1124中有结果3000。
该程序运行时,虚拟地址的变化情形如图(c)第2栏“虚拟地址”所示。如上所述,它代表了程序执行时的一种运行轨迹,是程序的一种动态特征。另一方面,每一个虚拟地址都有一个数对与之对应,如图(c)第3栏所示。把它里面的页号抽取出来,就构成了该程序运行时的页面走向,即如图(c)第4栏所示。它是描述程序运行时动态特征的另一种方法。从该程序的页面走向序列(0、1、0、2、0、1)可以看出,它所涉及的页面总数为6。注意页面总数的计算方法,只要从一页变成另一页,就要计数一次。
6.缺页中断率
假定一个作业允许的页面走向中涉及到的页面总数为A,其中有F次缺页,必须通过缺页中断把它们调入内存。
定义:f=F/A
f为“缺页中断率”
缺页中断率与缺页中断的次数有密切的关系。分析起来,影响缺页中断次数的因素有以下:
- 分配给作业的内存块数:由于分配给作业的内存块数多,同时能够装入内存的作业页面就多,缺页的可能性下降,发生缺页中断的可能性也就下降
- 页面尺寸:页面尺寸是与块尺寸相同的,因此块大页也就大。页面增大了,在每个内存块里的信息相应增加,缺页的可能性下降。反之,页面尺寸减小,每块里的信息减少,缺页的可能性上升。
- 程序的实现:作业程序的编写方法,对缺页中断产生的次数也会有影响。
页面淘汰算法
发生缺页时,就要从辅存上把所需要的页面调入到内存。如果当时内存中有空闲块,页面的调入问题就解决了;如果当时内存中已经没有空闲块可供分配使用,就必须在内存中选择一页,然后把它调出内存,以便为即将调入的页面让出块空间。这就是所谓的“页面淘汰”问题。
可以随机选择一个内存中的页面淘汰出去,但选择将来不常使用的页面,会使系统的性能更好一些。因为如果淘汰一个经常要使用的页面,那么很快又要用到它,需要把它再一次调入,从而增加了系统在处理缺页中断与页面调出/调入上的开销。
人们总是希望缺页中断少发生一些,如果一个刚被淘汰(从内存调出到辅存)出去的页,时隔不久因为又要访问它,又把它从辅存调入。调入不久再一次被淘汰,再访问,再调入。如此反复进行,会使整个系统一直陷于页面的调入、调出,以致大部分CPU时间都用于处理缺页中断和页面淘汰,很少能顾及到用户作业的实际计算。这种现象被称为“抖动”或称为“颠簸”。很明显,抖动使得整个系统效率低下,甚至趋于崩溃,是应该极力避免和排除的。
页面淘汰是由缺页中断引起的,但缺页中断不见得一定引起页面淘汰。只有当内存中没有空闲块时,缺页中断才会引起页面淘汰。
页面淘汰算法常见的有“先进先出页面淘汰算法”、“最久未使用页面淘汰算法”、“最少使用页面淘汰算法”以及“最优页面淘汰算法”等。
在内存里选中了一个淘汰的页面,如果该页面在内存时未被修改过,就可以直接用调入的页面将其覆盖掉;如果该页面在内存时被修改过,就必须把它回写到磁盘,以便更新该页在辅存上的副本。一个页面的内容在内存时是否被修改过,可以通过页表表目反映出来。
引用位:在系统规定的时间间隔内,该页是否被引用过的标志(该位在页面淘汰算法中将会用到)。
改变位:该位为“0”时,表示此页面在内存时,数据未被修改过;为“1”时,表示被修改过。当此页面被选中为淘汰对象时,根据此位的取值来确定是否要将该页的内容进行磁盘回写操作。
1.先进先出页面淘汰算法
先进先出(FIFO)是人们最容易想到的页面淘汰算法。其做法是进行页面淘汰时,总是把最早进入内存的页面作为淘汰的对象。
比如 一个作业运行的页面走向我
1、2、3、4、1、2、5、1、2、3、4、5
只分配给该作业3个内存块使用
运行后,通过3次缺页中断,把第1页、第2页、第3页这3个页面分别从辅存调入内存块中。当页面走向到达4时,用到第4页。由于3个内存块中没有第4页,因此需要通过缺页中断将其调入。但供该作业使用的3个内存块全部分配完毕,必须进行页面淘汰才能够腾空一个内存块,让所需的第4页进来。根据FIFO的淘汰原则,显然应该把第一个进来的第1页淘汰出去。紧接着又用到第1页,它不在内存的3个块中,于是不得不把这一时刻最先进来的第2页淘汰出去,依此类推。
作业涉及到的页面总数为12,缺页中断调入页面的次数是9
缺页中断率f是:
f=9/12=75%
在实际运用中,会经常把要访问的页面淘汰出去。为了尽量避免出现这种情况,又提出了对它的改进:“第二次机会页面淘汰算法”
这种算法的基础是先进先出
它把进入内存的页面按照先后次序组织成一个链表。在选择淘汰对象时,总是把链表的第1个页面作为要淘汰的对象,并检查该页面的“引用位(R)”。如果它的 R 位为“0”,表示从上一次页面淘汰以来,到现在它没有被引用过。这就是说,它既老又没用,因此可以把它立即淘汰;如果它的 R位为“1”,表示从上一次页面淘汰以来,它被引用过,因此暂时不淘汰它,再给它一次机会。于是将它的 R位修改为“0”,然后排到链表的最后,并继续在链表上搜索符合条件的淘汰对象。
第二次机会页面淘汰算法所做的,是在页面链表上寻找一个从上一次淘汰以来没有被访问过的页面。如果所有的页面都被访问过了,这个算法就成为纯粹的先进先出页面淘汰算法。极端地说,假如图(a)中所有页面的 R 位都是“1”,该算法就会一个接一个地把每一个页面移到链表的最后,并且把它的R位修改成“0”,最后又会回到页面A。此时它的R位已经是“0”,因此被淘汰出去。所以,这个算法总是能够结束的。
第二次集合页面淘汰算法要经常进行移动操作,影响系统的效率,可改进为循环链表的形式
用一个指针指向当前最先进入内存的页面。当发生缺页中断并要求页面淘汰时,首先检查指针指向的页面的R位。如果它的R位为“0”,就把它淘汰,让新的页面进入它原来占用的内存块,并把指针按顺时针方向向前移动一个位置;如果它的R位为“1”,则将其 R位清为“0”,然后把指针按顺时针方向向前移动一个位置,重复这一过程,直到找到一个R位为“0”的页面为止。它实际上是第二次机会页面淘汰算法的变形,有时称为“时钟页面淘汰算法”。
2.最近最久未用页面淘汰算法
最近最久未用(LRU)页面淘汰算法:在进行页面淘汰时,检查这些淘汰对象的被访问时间,总是把最长时间未被访问过的页面淘汰出去。这是一种基于程序局部性原理的淘汰算法。也就是说,该算法认为如果一个页面刚被访问过,不久的将来它被访问的可能性就大;否则被访问的可能性就小。
按照页面走向,从第1页面开始直到第5页面(即1,2,3,4,1,2,5),这两个图是一样的。当又进到第1页时,由于该页在内存块中,不会引起缺页中断,但是两个算法的处理就不相同了。
对于LRU,关心的是被访问的时间。
对第1页的访问,表明它是当前刚被访问过的页面,其次访问的是页面5,最早访问的是页面2。因此按照 LRU的原则,应该对它们在图(a)中的排列顺序加以调整才对。
即:第1页应该在最上面,第5页应该在中间,第2页应该在最下面。再进到页面2,仍然不发生缺页中断,对于FIFO,不用调整在内存中3页的先后次序;对于LRU,则又要调整这3页的排列次序。正是因为如此,当进到第3页、而第3页又不在内存时,不仅发生缺页中断,而且引起页面淘汰。在FIFO中,淘汰的对象是第1页;在LRU中,淘汰的对象则是第5页,后面的处理过程与此类似。
f=10/12=83%
实行FIFO页面淘汰算法要比LRU好,因为FIFO的缺页中断率小于LRU。
3.最近最少用页面淘汰算法
最近最少用(LFU)页面淘汰算法的着眼点是考虑内存块中页面的使用频率,它认为在一段时间里使用得最多的页面,将来用到的可能性就大。因此,当要进行页面淘汰时,总是把当前使用得最少的页面淘汰出去。
要实现LFU页面淘汰算法,应该为每个内存中的页面设置一个计数器。对某个页面访问一次,它的计数器就加1。经过一个时间间隔,把所有计数器都清0。产生缺页中断时,比较每个页面计数器的值,把计数器取值最小的那个页面淘汰出去。
4、最优页面淘汰算法
如果已知一个作业的页面走向,那么要进行页面淘汰时,应该把以后不再使用的或在最长时间内不会用到的页面淘汰出去,这样所引起的缺页中断次数肯定最小,这就是所谓的“最优(OPT)页面淘汰算法”。
比如,作业A的页面走向为(2、7、4、3、6、2、4、3、4…),分给它4个内存块使用。运行一段时间后,页面2、7、4、3分别通过缺页中断进入分配给它使用的4个内存块。当访问页面6时,4个内存块中已无空闲的可以分配,于是要进行页面淘汰。按照 FIFO 或 LRU等算法,应该淘汰第2页,因为它最早进入内存,或最长时间没有调用到。但是,稍加分析可以看出,应该淘汰第7页,因为在页面走向给出的可见的将来,根本没有再访问它!所以OPT肯定要比别的淘汰算法产生的缺页中断次数少。
遗憾的是,OPT的前提是已知作业运行时的页面走向,这是根本不可能做到的,所以OPT页面淘汰算法没有实用价值,它只能作为一个标杆(或尺度),与别的淘汰算法进行比较。如果在相同页面走向的前提下,某个淘汰算法产生的缺页中断次数接近于它,就可以说这个淘汰算法不错;否则就属较差。
异常现象
有若干因素会影响缺页中断的发生次数。因素之一是“分配给作业的内存块数”,并且“分配给作业的内存块数增多,发生缺页中断的可能性就下降”。这个结论对于FIFO页面淘汰算法来说,有时会出现异常。也就是说,对于FIFO页面淘汰算法,有时增加分配给作业的可用内存块数,它的缺页次数反而上升,通常称为“异常现象”。
FIFO的异常现象:
例:
给出某作业的页面走向是(4、3、2、1、4、3、5、4、3、2、1、5)。运行时,分别实行FIFO和LRU页面淘汰算法,试就3个内存块和4个内存块的情形,求出各自的缺页中断率,并分析对于FIFO是否会发生异常现象。
FIFO:当内存块为3时,缺页中断率f =9/12;当内存块为4时,缺页中断率f = 10/12
LRU:当内存块为3时,缺页中断率f =10/12;当内存块为4时,缺页中断率f = 9/12
请求分页式存储管理的特点:
- 它具有分页式存储管理的所有特点
- 它不仅打破了占据连续存储区的禁锢,而且打破了要求作业全部进入内存的禁锢。由于它能够向用户作业提供虚拟存储器,从而解决了小内存与大作业的矛盾。
缺点: 平均每一个作业仍要浪费半页大小的存储块,也就是说,请求分页式存储管理会产生内部碎片。