存储器是计算机系统的重要组成部分,是计算机系统中的一种宝贵而紧俏的资源。操作系统中的存储管理是指对内存(又称主存。默认情况下,本章的存储器指的是内存)的管理,是操作系统的重要功能之一。
应用程序从用户编写的源文件到内存中执行的进程,大致分为 3 个阶段:首先,经过编译程序(Compiler)将源代码编译为若个目标模块 (Object Module);其次,通过链接程序(Linker)将编译好的目标模块以及所需的库函数链接在一起,形成完整的装入模块(LoadModule);最后,通过装入程序(Loader)将这些装入模块装入内存并执行。简单来说,从源程序到执行的进程,经历了编译、链接、装入三个步骤。
程序的链接有以下3种方式:
程序的装入有以下3种方式:
绝对装入。在编译时就知道程序将要驻留在内存的物理地址,编译程序产生含有物理地址的目标代码。这种方式不适合多道程序设计。
可重定位装入。根据内存当前情况,将装入模块装入到内存的适当位置,地址变换通常在装入时一次完成,之后不再改变,这种方式也称为静态重定位。静态重定位的实现很简单,当操作系统为程序分配了一个以某地址为起始地址的连续主存区域后,重定位时将程序中指令或操作数的逻辑地址加上这个起始地址就得到了物理地址。
动态运行装入。允许程序运行时在内存中移动位置。装入模块装入到内存后的所有地址都是相对地址,在程序执行过程中,只有当访问到相应指令或数据时,才将要访问的程序或数据的相对地址转换为物理地址。由于重定位过程是在程序执行期间随着指令的执行逐步完成的,因此也称为动态重定位。动态重定位的实现要依靠硬件地址变换机构,最简单的实现方法是利用一个重定位寄存器。当某个作业开始执行时,操作系统负责把该作业在主存中的起始地址送入重定位寄存器中,之后在作业的整个执行过程中,每当访问内存时,系统就会自动将重定位寄存器的内容加到逻辑地址中去,从而得到与该逻辑地址对应的物理地址。
静态重定位的特点是容易实现,无须增加硬件地址变换机构。但它要求为每个程序分配一个连续的存储区,如果空间不足以放下整个程序就不能分配,而且在程序执行期间不能移动,不能再申请内存空间,难以做到程序和数据的共享。
动态重定位的特点是可以将程序分配到不连续的存储区中,在程序运行之前装入它的部分代码即可投入运行,然后在程序运行期间根据需要动态申请分配内存,便于程序段的共享可以向用户提供一个比主存的存储空间大得多的地址空间。动态重定位需要附加硬件支持,且实现存储管理的软件算法比较复杂。
在重定位中,逻辑地址向物理地址的转化公式为:
物理地址=基址寄存器内容+逻辑地址
逻辑地址(Logical Address)是指程序产生的与段(与页关因为只有对用户可见)相关的偏移地址部分。源代码在经过编译后,目标程序中所用的地址就是逻辑地址,而逻辑地址的范围就是逻辑地址空间。在编译程序对源代码进行编译时,总是从0号单元开始编址,地址空间中的地址都是相对于0开始的,因此逻辑地址也称为相对地址。在系统中运行的多个进程可能会有相同的逻辑地址,但这些逻辑地址映射到物理地址上时就变为了不同的位置。
物理地址 (Physical Address)是指出现在CPU外部地址总线上的寻物理内存的地址信号,是逻辑地址变换后的最终结果地址,物理地址空间是指内存中物理地址单元的集合。进程在运行过程中需要访问存取指令或数据时,都是根据物理地址从主存中取得。物理地址对于一般的用户来说是完全透明的,用户只需要关心程序的逻辑地址就可以了。从逻辑地址到物理地址的转换过程由硬件自动完成,这个转换过程叫作地址重定位。
内存保护是为了防止一个作业有意或无意地破坏操作系统或其他作业。常用的内存保护方法有界限寄存器方法和存储保护键方法。
界限寄存器方法
采用界限寄存器方法实现内存保护又有上,下界寄存器方法以及基址和限长寄存器方法两种实现方式。
存储保护键方法
给每个存储块分配一个单独的保护键,其作用相当于一把“锁”。不同于分区存储块,一个分区由若干存储块组成,每个存储块大小相同,一个分区的大小必须是存储块的整数倍。此外,进入系统的每个作业也被赋予一个保护键,它相当于一把“钥匙”。当作业运行时,检查“钥匙”和“锁”是否匹配,如果二者不匹配,则系统发出保护性中断信号,并停止作业的运行。
覆盖(Overlay)技术主要用在早期的操作系统中,因为在早期的单用户系统中内存的容量一般很小,可用的存储空间受到限制,某些大作业不能一次全部装入内存中,这就产生了大作业与小内存的矛盾,为此引入了覆盖技术。
所谓覆盖技术,就是把一个大的程序划分为一系列覆盖,每个覆盖是一个相对独立的程序单位。把程序执行时并不要求同时装入内存的覆盖组成一组,称为覆盖段;将这个覆盖段分配到同一个存储区域,这个存储区域称为覆盖区,它与覆盖段一一对应。显然,为了使一个覆盖区能被相应覆盖段中的每个覆盖在不同时刻共享,其大小应由覆盖段中的最大覆盖来确定。
A和B模块组成覆盖段1,C、D和E模块组成覆盖段2。为了实现真正覆盖,相应的覆盖区应为每个覆盖段中最大覆盖的大小。采用覆盖技术后,运行该用户程序总共只需80KB的内存。
交换(Swapping)是指把暂时不用的某个程序及数据部分(或全部)从内存移到外存中,以便腾出必要的内存空间,或把指定的程序或数据从外存读到相应的内存中,并将控制权转给它,让其在系统上运行的一种内存扩充技术。处理器三级调度中的中级调度就是采用了交换技术。
交换技术的特点是打破了一个程序一旦进入主存便一直运行到结束的限制,但运行的进程大小仍然受实际主存的限制。
什么是内部碎片与外部碎片?
根据碎片出现的情况,可以将碎片分为内部碎片和外部碎片。内部碎片是指已经分配给作业但不能被利用的内存空间,外部碎片是指系统中还没有分配给作业,但由于碎片太小而无法分配给申请内存空间的新进程的存储块,如下面讲到的固定分区分配中存在内部碎片,而动态分区分配中存在外部碎片。通俗点理解就是,某个作业所占用的内存区域如果没有装满,就是内部碎片,而作业与作业之间,如果有内存区域没有分配给某个作业,但又不能分配给任何作业,就是外部碎片。
单一连续分配是一种最简单的存储管理方式,通常只能用于单用户、单任务的操作系统中。这种存储管理方式将内存分为两个连续存储区域,其中一个存储区域固定地分配给操作系统使用,通常放在内存低地址部分,另一个存储区域给用户作业使用。通常,用户作业只占用所有分配空间的一部分,剩下一部分实际上浪费掉了。
单一连续分配方式采用静态分配,适合单道程序,可采用覆盖技术。作业一旦进入内存,就要等到其结束后才能释放内存。因此,这种分配方式不支持虚拟存储器的实现,无法实现多道程序共享主存。
单一连续分配方式的优点是管理简单,只需要很少的软件和硬件支持,且便于用户了解和使用,不存在其他用户干扰的问题。其缺点是只能用于单用户、单任务的操作系统,内存中只装入一道作业运行,从而导致各类资源的利用率都很低。单一连续分配会产生内部碎片。
固定分区分配(也称为固定分区存储管理) 方法是最早使用的一种可运行多道程序的存储管理方法,它将内存空间划分为若干个固定大小的分区,每个分区中可以装入一道程序。分区的大小可以不等,但事先必须确定,在运行时不能改变。当有空闲分区时,便从后备队列中选择一个适当大小的作业装入运行。固定分区分配中,程序通常采用静态重定位方式装入内存。为了实现固定分区分配,系统需要建立一张分区说明表,以记录可用于分配的分区号、分区的大小、分区的起始地址及状态,通常按照分区的大小顺序排序。
当某个用户程序要装入内存时,由内存分配程序检索分区说明表,从表中找出一个能满足要求的尚未分配的分区分配给该程序,然后修改分区说明表中相应分区表项的状态;若找不到大小足够的分区,则拒绝为该程序分配内存。当程序执行完毕不再需要内存资源时,释放程序占用的分区,管理程序只需将对应分区的状态设置为未分配即可。分区的大小可以相等,也可以不相等。
固定分区分配的优点是可用于多道程序系统最简单的存储分配,其缺点是不能实现多进程共享一个主存区,利用率较低,会产生内部碎片。
动态分区分配又称为可变式分区分配,是一种动态划分存储器的分区方法。这种分配方法并不事先将主存划分成一块块的分区,而是在作业进入主存时,根据作业的大小动态地建立分区,并使分区的大小正好满足作业的需要。因此,系统中分区的大小是可变的,分区的数目也是可变的。
分区分配中的数据结构
为了实现动态分区分配,系统中也必须设置相应的数据结构来记录内存的使用情况。常用的数据结构形式如下。
分区分配算法
为了将一个作业装入内存,应按照一定的分配算法从空闲分区表(或空闲分区链)中选出一个满足作业需求的分区分配给作业。如果这个空闲分区的容量比作业请的空间容量要大,那么将该分区的一部分分配给作业,剩下的一部分仍然留在空闲分区表(或空闲分区链)中,同时需要对空闲分区表(或空闲分区链)中的有关信息进行修改。
分区的回收
当作业执行结束时,系统应回收已使用完毕的分区。系统根据回收分区的大小及首地址,在空闲分区表(或空闲分区链)中检查是否有相邻的空闲分区,如有相空闲分区,则合并成一个大的空闲区,并修改有关的分区状态信息。回收分区与已有空闲分区的相邻情况有以下4种:
分区分配的动态管理
在分区存储管理方式中,必须把作业装入到一片连续的内存空间中。如果系统中有若干小的分区,其总容量大于要装入的作业,但由于它们不相邻接,致使作业不能装入内存。例如,内存中有4 个空分区不邻接,它们的大小分别为 20KB、30KB、15KB、25KB,总大小为90KB。但如果有一个40KB 的作业到达,因系统中所有空闲分区的容量均小于40KB故此作业无法装入内存,所以就需要对分区分配进行动态管理。
拼接技术。所谓碎片(也可称为零头),是指内存中无法被用的储空间。在分区存储管理方式下,系统运行一段时间后,内存中的碎片会占据相当数量的空间。
解决碎片问题的方法之一是将存储器中所有已分配分区移动到主存的一端,使本来分散的多个小空闲区连成一个大的空闲区。这种通过移动把多个分散的小分区拼接成一个大分区的方法称为拼接或紧凑,也可称为紧缩。
除了有怎样进行拼接的技术问题外,拼接技术的实现还存在一个拼接时机的问题,这个问题有两种解决方案。
动态重定位分区分配技术。动态重定位分区分配算法与动态分区分配算法基本相同,两者的差别仅在于:在这种分配算法中增加了拼接功能,通常是在找不到足够大的空闲分区来满足作业要求,而系统中空闲分区容量总和大于作业要求时进行拼接。
动态分区分配的优缺点
非连续分配允许一个程序分散地装入到不相邻的内存分区中。在连续分配管理方式中我们发现,即使内存有超过1GB 的空闲空间,但如果没有连续的 1GB 的空间,需要 1GB 空间的作业仍然是无法运行的:但如果采用非连续分配方式,作业所要求的 1GB 内存空间可以分散地分配在内存的各个区域,当然这也需要额外的空间去存储它们(分散区域)的索引,使得非连续分配方式的存储密度低于连续存储方式。
非连续分配管理方式根据分区大小是否固定分为分页存储管理方式和分段存储管理方式,其中分页存储管理方式根据运行作业时是否需要把作业的所有页都装入内存才能运行而分为基本分页存储管理方式和请求分页存储管理方式。
在分区存储管理中,要求把作业放在一个连续的存储区中,因而会产生碎片问题(外部碎片)。尽管通过拼接技术可以解决碎片问题,但代价较高。如果允许将一个作业存放到许多不相邻接的分区中,那么就可以避免拼接,从而有效地解决外部碎片问题。基于这一思想,引入了分页存储管理(或称页式存储管理) 技术。
分页原理
在分页存储管理中,用户作业的地址空间被划分成若干个大小相等的区域,称为页或页面。相应地,将主存的存储空间也分成与页面大小相等的区域,称为块或物理块。在为作业分配存储空间时,总是以块为单位来分配,可以将作业中的任意一页放到主存的任意一块中。
主存中与页面大小相等的物理块也可称为页框。
在调度作业运行时,必须将它的所有页面一次调入主存;若主存中没有足够的物理块则作业等待。这种存储管理方式称为简单分页或纯分页。
页面的大小由机器的地址结构决定。在确定地址结构时,若选择的页面较小,可使页内碎片较小并减少内存碎片的总空间,有利于提高内存利用率,但也会使每个进程要求较多的页面,从而导致页表过长,占用较多内存,还会降低页面换进换出的效率。若选择的页面较大,虽然可以减少页表长度,提高页面换进换出的效率,但却会使页内碎片增大。
因此,页面的大小应选择适中(通常为 2 的整数幂),以方便地址变换,一般为 512B~4KB。
分页存储管理系统中的逻辑地址(见图 3-10)含两部分内容:前一部分为页号P,后一部分为页内位移W(也称为页内偏移量)。
上述地址结构中,两部分构成的地址长度为 32位。其中0~11 位是页内位地址,即每页的大小为212B=4KB,12~31 位是页号,即一个进程运行的最多页数为220=1M 页。
假设逻辑地址为 A,页面大小为 L,则页号 P= (int)(A/L),页内位 W=A%L。其中“(int)”是强制类型转换为整型,“/”为取商操作,“%”是取余操作。
页表
为了将逻辑地址上连续的页号映射到物理内存中后成为离散分布的多个物理块,需要将每个页面和每个物理块一一对应,这种映射关系就体现在页表上。页表中每个页表项都由页号和块号组成,根据页表项就可以找到每个页号所对应物理内存中物理块的块号。页表通常存放在内存中。
假设一个用户程序在基本分页储管理方式中被分成n页如图3-11a 所示,则其页号与内存中的块号的对应关系如图3-11b 中页表所。根据页表所的对应关系,用户程序的每一页分别存储于图 3-11c 的内存区域中。
基本地址变换机构
整个地址变换过程都是由硬件自动完成的。
页表寄存器(PTR):用来存放页表在内存中的起始地址和页表的长度。
假设页面大小为L、页表长度为 M、地址为A、地址为 E,下面列出从逻辑地址 A得到物理地址 E访内存的步骤。
具有块表的地址变换机构
**若页表全部放在主存中,则存取一个数据或一条指令至少要访问两次主存。其中,第一次是通过访问页表以确定所存取的数据或指令的物理地址,第二次是根据所得到的物理地址存取数据或指令。**显然,这种方法比通常执行指令的速度慢了一半。
为了提高地址变换的速度,可以在地址变换机构中增设一个具有并行查找功能的高速缓冲存储器(又称联想存储器或快表)将部分页表项放在这个高速缓冲存储器中快表(TLB),一般是由半导体存储器实现的,其工作周期与 CPU的周期大致相同,但造价较高。为了降低成本,通常是在快表中存放正在运行作业当前访问的那些页表项,页表的其余部分仍然存放在内存中。
两级页表和多级页表
页表大小的计算。在基础分页系统中,页表长度 M 页是由页号的位数决定的。而页表的大小可以理解成一个矩形的面积,这个矩形的长度就是页表长度 M,宽度是每个页表项的大小,即块号的位数。
两级页表。从页表大小的计算公式可知,页表大小和页表长度成正比,而页表长度又随着页号位数的增长而呈指数式增长。所以,如果系统的逻辑地址的位数较多,页表会非常大,而整张页表都需要连续地存放在内存中,这是件很困难的事情,于是就有了两级页表。
两级页表的系统将逻辑地址划分为外层页号、外层页内地址和页内地址。先用外层页号 P1在外部页表上查找,找出的单元内容是二级页表的首地址,页表的首地址加上外层页内地址 P2 就是页表项的地址,取出里面的数值(即物理块号),物理块号与页内地址 d 相组合就得到了物理地址。在两级页表机制下,无论是外层页号的位数还是外层页内地址的位数,都比一级页表机制少了很多,这样页表的长度就可以减少很多,相应页表的大小也大大减小了。
多级页表
对于32位的机器(逻辑地址的位数时32位),采用两级页表机构是合适的,但对于64 位的系统,两级页表机构会使页表的大小变得不可接受,所以可以通过继续增加页表的级数来减小页表的大小,不过会使页表的数量大大增加。多级页表最主要的缺点是要多次访问内存,每次地址变换很浪费时间。
页的共享与保护
在多道程序系统中,数据的共享是很重要的。在分页存储管理系统中,实现共享的方法是使共享用户地址空间中的页指向相同的物理块。
在分页存储管理系统中实现共享比在分段系统中要困难。因为在分页存储管理系统中,将作业的地址空间划分成页面的做法对用户是透明的,同时作业的地址空间是线性连续的,当系统将作业的地址空间分成大小相同的页面时,被共享的部分不一定被包含在一个完整的页面中,这样不应共享的数据也被共享了,不利于保密。另外,共享部分的起始地址在各作业的地址空间划分成页的过程中,在各自页面中的页内位移可能不同,这也使得共享比较难。
分页存储管理系统可以为内存提供两种保护方式:
基本分页存储管理方式的优缺点
分段存储管理相较于分页存储管理有如下优点:
分段存储原理
在分段存储管理系统中,作业的地址空间由若干个逻辑分段组成,每个分段是一组逻辑意义上相对完整的信息集合,每个分段都有自己的名字,每个分段都从0开始编址,并采用一段连续的地址空间。因此,整个作业的地址空间是二维的(段的分类是一维,段内位移是另一维)。分段存储管理中以段为单位分配内存,每段分配一个连续的内存区,但各段之间不要求连续。内存的分配与回收类似于动态分区分配。
分段存储管理系统的逻辑地址结构由段号 S 和内位 W(也叫段内偏移量)组成
段号S 通常是从0开始的连续正整数。当逻辑地址结构中段号和段内位移占用的位数确定之后,一个作业地址空间中允许的最大段数和各段的最大长度也就确定了。
例如,在图 3-15中,段号占用的位数为 16 位,段内位移占用的位数也是 16 位,因此一个作最多可以有216=65536段,大段长为64KB。
分页存储管理系统的地址空间是一维的,而分段存储管理系统是二维的
段表及地址变换过程
与分页存储管理类似,为了实现从逻辑地址到物理地址的变换,系统为每个进程建立个段表,其中每个表项描述一个分段的信息,表项中包含段号、段长和该段的内存起始地址。
在配置了段表后,执行中的进程可通过查找段表,找到每个段所对应的内存区域,实现从逻辑段到物理内存区的映射
地址变换过程都是由硬件自动完成的
从逻辑地址 A 得到物理地址 E 再访问内存的如下:
比较段号S 和段表长度M, 若S>=M,则产生越界中断,否则转到3)继续执行
段的共享与保护
在分段存储管理系统中,分段的共享是通过使多个作业的段表中相应表项都指向被共享段的同一个物理副本来实现的。
与分页管理类似,分段管理的保护主要有两种:地越界保护和访问控制保护
基本分段存储管理方式的优缺点
分段与分页的区别
分页存储管理与分段存储管理有许多相似之处,例如,两者都采用离散分配方式,且都要通过地址变换机构来实现地址变换。但两者在概念上也有很多区别
从上面的介绍中可以看出,分页系统能有效地提高了内存利用率并能解决碎片问题,而分段系统能反映程序的逻辑结构并有利于段的共享。如果将这两种存储管理方式结合起来,就形成了段页式存储管理方式。
在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每一段分成若干个大小固定的页。对于主存空间的管理仍然和分页管理一样,将其分成若干个和页面大小相同的物理块,对主存的分配以物理块为单位。
段页式存储管理系统的逻辑地址结构包含 3 部分内容:段号S 段内页号P和页内位移D
为了实现地址变换,段页式存储管理系统中需要同时设立段表和页表。系统为每个进程建立一张段表,而每个分段有一张页表。段表的表项中至少应包括段号、页表始址和页表长度,其中页表始址指出该段的页表在主存中的起始位置;页表的表项中至少应包括页号和块号。此外,为了便于实现地址变换,系统中还需要配置一个段表寄存器,用来存放段表的起始地址和段表长度。
逻辑地址A得到物理地址E再去访问内存的步骤:
由上述步骤可知,段页式存储管理系统的地址变换需要访问内存3 次,所以同样可以用高速缓冲寄存器(快表)来减少对内存的访问次数。
段页式的确结合了段式和页式的优点,而且克服了段式的外部碎片问题,但是段页式的内部碎片并没有做到和页式一样少。页式存储管理方式下平均一个程序有半页碎片,而段页式存储管理方式下平均一段就有半页碎片,而一个程序往往有很多段,所以平均下来段页式的内部碎片比页式要多。
前面介绍的若干种存储管理方法都是分析如何将多个程序装入内存中并行。这些方法都具有如下两个特点:一次性(作业全部装入内存后才能执行)和驻留性(作业常驻内存直到运行结束)。它们均难以满足较大的作业或者较多的作业进入内存执行。
而程序在执行过程中,有些代码是较少用到的(比如错误处理部分),而且有的程序需要较长时间的 I/O 处理,从而导致了很多内存空间的浪费。为此引入了一种能够让作业部分装入就可以运行的存储管理技术,即虚拟内存管理技术。
大多数程序执行时,在一个较短的时间内仅使用程序代码的一部分,相应地,程序所访问的存储空间也局限于某个区域,这就是程序执行的局部性原理。
基于局部性原理,在程序装入时,一方面可以将程序的一部分放入内存,而将其余部分放在外存,然后启动程序(部分装入)。在程序执行过程中,当所访问的信息不在内存中时,再由操作系统将所需的部分调入内存(请求调入)。另一方面,操作系统将内存中暂时不使用的内容置换到外存上,从而腾出空间存放将要调入内存的信息(置换功能)。从效果上看,计算机系统好像为用户提供了二个存储容量比实际内存大得多的存储器,这种从逻辑上扩充内存容量的存储器系统称为虚拟存储器(简称虚存)。
将其称为虚拟存储器是因为这种存储器实际上并不存在,系统只是提供了部分装入、请求调入和置换功能,给用户的感觉是好像存在一个能满足作业地址空间要求的内存。
虚拟内存的意义是让程序存在的地址空间与运行时的存储空间分开,程序员可以完全不考虑实际内存的大小,而在地址空间内编写程序。虚拟存储器的容量由计算机的地址结构决定,并不是无限大。
虚拟内存的特征
常用的虚拟存储技术有请求分页存储管理、请求分段存储管理和请求段页式存储管理
分页存储管理方式虽然解决了内存中的外部碎片问题,但它要求将作业的所有页面一次性调入主存。当主存可用空间不足或作业太大时,就会限制一些作业进入主存运行。为此引入了请求分页(也称请求页式)存储管理方式,先将程序部分载入内存执行,当需要其他部分时再调入内存。很明显,这种方法是根据程序的局部性原理产生的。
请求分页存储管理方法在作业地址空间的分页、存储空间的分块等概念上和分页存储管理完全一样。它是在分页存储管理系统的基础上,通过增加请求调页功能、页面置换功能所形成的一种虚拟存储系统。在请求分页存储管理中,作业运行之前,只要将当前需要的一部分页面装入主存,便可以启动作业运行。在作业运行过程中,若所要访问的页面不在主存中,则通过调页功能将其调入,同时还可以通过置换功能将暂时不用的页面置换到外存上,以便腾出内存空间。
请求分页=基本分页+请求调页功能+页面置换功能
在请求分页系统中使用的主要数据结构仍然是页表,其基本作用是将程序地址空间中的逻辑地址转换成内存空间中的物理地址。由于请求分页系统只将作业的一部分调入内存,还有一部分存放在磁盘上,故需要在页表中增加若干项,以供操作系统在实现页面的调入、换出功能时参考。
在请求分页存储管理系统中,若所访问的页面在内存中,其地址变换过程与分页存储管理相同;若访问的页面不在内存,则应先将该页面调入内存,再按照与基本分页存储管理相同的方式进行地址变换。
若系统发现所要访问的页面不在内存中,便产生一个缺页中断信号,此时用户程序被中断,控制转到操作系统的缺页中断处理程序。缺页中断处理程序根据该页在外存的位置将其调入内存。在调页过程中,若内存中有空闲空间,则缺页中断处理程序只需把缺页装入任何一个空闲存储块中,再对页表中的相应表项进行修改(如填写物理块号、修改状态位、设置访问字段及修改位初值等)即可;若内存中无空闲空间,则必须先淘汰内存中的某些页面,若被淘汰页曾被修改过,则要将其写回外存。
缺页中断是一个比较特殊的中断,它与一般中断相比有着明显的区别,主要表现在以下方面:
页面置换算法(也称为页面淘汰算法) 是用来选择换出页面的算法。在请求页式存储管理方式中,由于一个进程运行时不是所有页面都在内存中,因此会出现缺页中断,若此时内存没有空闲的物理块,就需要置换出内存中的一页,具体置换出哪一页面是由页面置换算法决定的,由此可见,页面置换算法的优劣直接影响系统的效率。
在预知一个进程的页面号引用串的情况下,每次都淘汰以后不再使用的或以后最迟再被使用的页面,这种算法就是最佳置换算法。
显然,最佳置换算法是最优的,具有最低的缺页率。但由于实际操作中往往无法事先知道以后会引用到的所有页面的信息,因此最佳置换算法无法实现,只能作为一个标准来衡量其他置换算法的优劣。
页面顺序:3,4,2,6,4,3,7,4,3,6,3,4,8,4,6
FIFO算法是最简单的页面置换算法,每次总是淘汰最先进入内存的页面,即淘汰在内存驻留时间最长的页面。
该算法实现简单,用一个队列的数据结构就可以实现,将页面按照次序排成一个队列,并设置指针指向最先进入的页面,每次需要淘汰页面时,将指针所指的页面淘汰即可。
不过 FIFO算法可能会产生 Belady 异常(缺页次数随分配的物理块号的增加而增加),这是由于 FIFO 算法忽略了一种现象,就是最早调入的页面往是使用最频繁的页面,因此 FIFO算法与进程实际运行规律不符,可能选择淘汰的页面是程序经常使用的页面,实际效果不好。
选择最近最长时间没有被使用的页面予以淘汰,其思想是用以前的页面引用情况来预测将来会出现的页面引用情况,即假设一个页面刚被访问,那么不久该页面还会被访问。因此最佳置换算法是“向后看”,而最近最少使用法则是“向前看”。
该算法可以用寄存器组和栈来实现,性能较好。在常用的页面置换算法中,LRU算法最接近最佳置换算法。
时钟置换算法也称为最近未使用(NRU)算法,是 LRU 和 FIFO 的中。作为 LRU 的近似算法,CLOCK 算法给每个页面设置一个访问位,用以标识该页最近有没有被访问过。CLOCK维护一个内存中所有页面的循环链表,当程序需要访问链表中存在的页面时,该页面的访问位就被置位为 1;否则,若程序要访问的页面没有在链表中,那就需要淘汰一个内存中的页面,于是一个指针就从上次被淘汰页面的下一个位置开始顺序地去遍历这个循环链表,当这个指针指向的页面的访问位为 1 时,就把该访问位清零,指针再向下移动,当指针指向的页面的访问位为 0时,就选择淘汰掉这一页面,若遍历了一遍链表仍没找到可以淘汰的页面,则继续遍历下去。
还有一种改进型CLOCK 算法,它考虑了页面载入内存后是否被修改的问题,增加了修改位。在访问位同为 0 的进程间优先淘汰没有修改过的页面,因为没有修改过的页面可以被直接淘汰掉,而修改过的页面需要写回到外存中。与简单 CLOCK 算法相比,该算法可减少磁盘 I/O 次数,但会增加扫描次数。
改进型CLOCK算法增加了修改位后,每个页面的状态存在如下4种情况。
改进型时钟算法的算法步骤如下:
最不常用置换(LFU)算法
选择到当前时间为止访问次数最少的页面淘汰。该算法要求为每页设置一个访问计数器,每当页面被访问时,该页的访问计数器加 1。发生缺页中断时,淘汰计数值最小的页面,并将所有计数器清零。
页面缓冲算法
PBA 算法是对 FIFO 算法的发展,通过建立置换页面的缓冲,找回刚被置换的页面,从而减少系统I/O 的消耗。PBA 算法用 FIFO 算法选择被置换页,选择出的页面不是立即换出而是放入两个链表之一中。如果页面未被修改,就将其归入到空闲页面链表的末尾,否则将其归入到已修改页面链表的末尾。这些空闲页面和已修改页面会在内存中停留一段时间。如果这些页面被再次访问,只需将其从相应链表中移出,就可以返回给进程,从而减少了一次磁盘 I/O。需要调入新的物理页时,将新页面读入到空闲页面链表的第一个页面中,然后将其从该链表中移出。当已修改页达到一定数目后,再将其一起写入磁盘,然后将它们归入空闲页面链表。这样能大大减少 I/O 操作的次数。
为了解决抖动现象,引入了工作集的概念。工作集是基于局部性原理假设的。如果能预知程序在某段时间间隔内要访问哪些页面,并能提前将它们调入内存,将会大大降低缺页率,从而减少置换工作,提高 CPU 利用率。
工作集是最近 n 次内存访问的页面的集合,数字n称为工作集窗口,也就是工作集的大小。经常被使用的页面会在工作集中,若一个页面不再使用,则它会在工作集中被丢弃。当一个进程寻址一个不在工作集内的页面时,会产生一个缺页中断。在处理缺页中断时,更新工作集并在需要时从磁盘中读入此页面。
工作集模型的原理是:让操作系统监视各个进程的工作集,主要是监视各个工作集的大小。若有空闲的物理块,则可以再调一个进程到内存以增加多道的程度;若工作集的大小总和增加超过了所有可用物理块的数量总和,则操作系统可以选择一个内存中的进程对换到磁盘中去,以减少内存中的进程数量来防止抖动的发生。
正确选择工作集窗口大小,即分配给进程的页面数,对存储器的有效利用率和系统吞吐率的提高都将产生重要影响
在请求分页存储管理系统中,可以采用两种页面分配策略,即固定分配和可变分配。在进行页面置换时,也可以采用两种策略,即全局置换和局部置换。将它们组合起来,有如下3种适合的策略(固定分配全局置换不合理,因此不存在这种策略)。
固定分配局部置换
为每个进程分配一定数目的物理块,这个数目是确定的,进程运行期间都不会改变。这样,进程之间不会争夺物理块,会导致有些进程因为物理块太少而频繁地缺页中断,有些进程由于分配的物理块太多而浪费内存空间。采用固定分配局部置换策略时,需要用算法决定每个进程分配多少块物理块,常用的算法有平均分配算法、按比例分配算法以及考虑优先权的分配算法。
可变分配全局置换。操作系统维护一个空闲物理块队列,每次有进程缺页时都从空闲物理块队列上取下一个分配给它,如果系统中已经没有空闲的物理块了,那么系统将有可能调出任何进程中的其中一页。
可变分配局部置换。为每个进程分配一定量的物理块后,每次发生缺页中断且内存中没有空闲物理块时,只让进程换出自己的某个内存页,但当一个进程频繁地发生缺页中断时,OS 为它分配额外的物理块,直到缺页率降低到合适程度为止,当一个进程缺页率特别低时,则适当减少分配给它的物理块的数量。可变分配局部置换策略在可以获得较高的内存空间利用率的同时,保证每个进程有较低的缺页率。
请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。通常,由于对换区是采用连续分配方式,而文件是采用离散分配方式,因此对换区的磁盘 I/O 速度比文件区的高。这样,每当发生缺页请求时,系统应从何处将缺页调入内存。
FIFO置换算法的缺页率可能会随着所分配的物理块数的增加而增加,这种奇怪的现象就是Belady 异常。例如,对于引用串1,2,3,4,1,2,5,1,2,3,4,5,内存中物理块数为3时发生9次缺页中断,而物理块数为4时反倒会发生10 次缺页中断。
产生 Belady 异常的原因是 FIFO算法的置换特征与进程访问内存的动态特征相矛盾,即被置换的页面并不是进程不会访问的。
FIFO算法可能出现Belady异常,而 LRU 算法和最佳置换算法永远不会出现 Belady异常,被归类为堆栈算法的页面置换算法也不可能出现 Belady 异常。
若选用的页面置换算法不合适,可能会出现这种现象:刚被淘汰的页面,过后不久又要访问,并且调入不久后又调出,如此反复,使得系统把大部分时间用在了页面的调入调出上.而几乎不能完成任何有效的工作,这种现象称为抖动(或颠簸)。
抖动产生的原因是在请求分页系统中的每个进程只能分配到所需全部内存空间的一部分。
假定一个作业共有 n页,系统分配给该作业 m页的空间(m<=n)。如果该作业在运行中共需要访问 A 次页面(即引用串长度为 A),其中所要访问的页面不在内存,需要将所需页调入内存的次数为F,则缺页率定义为f=F/A,命中率即为1-f。
缺页率是衡量页面置换算法的重要指标。通常缺页率会受置换算法、分配的页面数量、页面大小等因素的影响。缺页率对于请求分页管理系统是很重要的,如果缺页率过高,会直接导致读取页面的平均时间增加,使进程执行速度显著降低。因此,如何降低缺页率是一项非常重要的工作。
请求分段存储管理系统与请求分页存储管理系统一样,为用户提供了一个比主存可用空间大得多的虚拟存储器。同样,虚拟存储器的实际容量由计算机的地址结构确定。在请求分段存储管理系统中,作业运行之前,只要将当前需要的若干段装入主存,便可启动作业运行。在作业运行过程中,若要访问的分段不在主存中,则通过调段功能将其调入,同时还可以通过置换功能将暂时不用的分段置换到外存上,以便腾出内存空间。为此,应对段表进行扩充。
段号、段长和内存始址这 3 个信息是进行地址变换所必需的,其他字段的含义与请求分页存储管理相同。当段在内存中时,地址变换过程与分段存储管理相同;当段不在内存中时,应先将该段调入内存,然后再进行地址变换。
当被访问的段不在主存中时,将产生一个缺段中断信号。操作系统处理该中断时,在主存中查找是否有足够大的分区存放该段。若没有这样的分区,则检查空闲分区容量总和,确定是否需要对分区进行拼接,或者调出一个或几个段后再装入所需段。