操作系统(5)虚拟存储器

  一、虚拟存储器介绍

  前面介绍的各种存储器管理方式有一个共同的特点,即它们都要求将一个作业全部装入内存后方能运行。于是,出现了下面的两种情况:

  • 有的作业很大,其所要求的内存空间超过了内存总容量,作业不能全部被装入内存,致使该作业无法运行。
  • 有大量作业要求运行,但由于内存容量不足以容纳所有这些作业,只能将少数作业装入内存让它们先运行,而将其他大量的作业留在外存上等待。

  出现上面两种情况的原因都是由于内存容量不够大。一个显而易见的解决方法是从物理上增加内存容量,但这些往往会受到机器自身的限制,而且无疑要增加系统成本,因此这种方法是受到一定限制的。另一种方法并非是从物理上实际地扩大内存的容量,而是从逻辑上扩充内存容量,让用户所感觉到的内存容量比实际内存容量大得多。于是便可以让比内存空间更大的程序运行,或者让更多的用户程序并发运行。这样既满足了用户的需要,又改善了系统的性能。这正是虚拟存储技术所要解决的问题。

  1.常规存储管理方式的特征和局部性原理

  (1)常规存储器管理方式的特征

  • 一次性。是指作业必须一次性地全部装入内存后方能开始运行。而正是这一特征导致了大作业无法在小内存中运行,以及无法进一步提高系统的多道程序度,直接限制了对处理机的利用率和系统的吞吐量的提高。事实上,许多作业在运行时,并非需要用到全部程序和数据,如果一次性地装入其全部程序和数据,显然也是对内存空间的一种浪费。
  • 驻留性,是指作业被装入内存后,整个作业都一直驻留在内存中,其中任何部分都不会被换出,直至作业运行结束。尽管运行中的进程会因 I/O 等原因而被阻塞,可能处于长期等待状态,或者有的程序模块在运行过一次后就不再需要(运行)了,它们都仍然驻留在内存中,继续占用宝贵的内存资源。

  由此可以看出,一次性及驻留性特征使得许多在程序运行中不用或暂不用的程序(数据)占据了大量的内存空间,使得一些需要运行的作业无法装入运行。现在要研究的问题是:一次性及驻留性在程序运行时是否是必需的。

  (2)局部性原理

  程序在执行时将呈现出局部性规律,即在一较短的时间内,程序的执行仅局限于某个部分;相应地,它所访问的存储空间也局限于某个区域。

  • 程序执行时,除了少部分的转移和过程调用指令外,在大多数情况下仍是顺序执行的。
  • 过程调用将会使程序的执行轨迹由一部分区域转至另一部分区域,但过程调用的深度在大多数情况下都不超过 5。这就是说,程序将会在一段时间内都局限在这些过程的范围内运行。
  • 程序中存在许多循环结构,这些结构虽然只由少数指令构成,但是它们将多次执行。
  • 程序中还包括许多对数据结构的处理,如对数组进行操作,它们往往都局限于很小的范围内。

局限性还表现在下述两个方面:

  • 时间局限性。如果程序中的某条指令一旦执行,则不久以后该指令可能再次执行;如果某数据被访问过,则不久以后该数据可能再次被访问。产生时间局限性的典型原因是由于在程序中存在着大量的循环操作。
  • 空间局限性。一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,其典型情况便是程序的顺序执行。

  (3)虚拟存储器的基本工作情况

  基于局部性原理,应用程序在运行之前,没有必要全部装入内存,仅须将那些当前要运行的少数页面或段先装入内存便可运行,其余部分暂留在盘上。程序在运行时,如果它所要访问的页(段)已调入内存,便可继续执行下去;但如果程序所要访问的页(段)尚未调入内存(称为缺页或缺段),便发出缺页(段)中断请求,此时程序应利用 OS 所提供的请求调页(段)功能,将它们调入内存,以使进程能继续执行下去。如果此时内存已满,无法再装入新的页(段),则还须再利用页(段)的置换功能,将内存中暂时不用的页(段)调至盘上,腾出足够的内存空间后,再将要访问的页(段)调入内存,使程序继续执行下去。这样,便可使一个大的用户程序能在较小的内存空间中运行;也可在内存中同时装入更多的进程使它们并发执行。

  2.虚拟存储器的定义和特征

  (1)虚拟存储器的定义

  当用户看到自己的程序能在系统中正常运行时,他会认为,该系统所具有的内存容量,一定比自己的程序大,或者说,用户所感觉到的内存荣爱玲将比实际内存容量大得多。但须说明,用户所看到的大容量只是一种感觉,是虚的,故人们把这样的存储器称为虚拟存储器。
  所谓虚拟存储器,是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。其逻辑容量由内存容量和外存容量之和所决定,其运行速度接近于内存速度,而每位的成本却又接近于外存。可见,虚拟存储技术是一种性能非常优越的存储器管理技术,故被广泛地应用于大、中、小型机器和微型机中。

  (2)虚拟存储器的特征

  • 多次性。多次性是相对于传统存储器管理方式的一次性而言的,是指一个作业中的程序和数据无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行,即只需将当前要运行的那部分程序和数据装入内存即可开始运行。以后每当要运行到尚未调入的那部分程序时,再将它调入。正是由于虚拟存储器的多次性特征,才使它具有从逻辑上扩大内存的功能。毫无以为的是,多次性是虚拟存储器最重要的特征,任何其它的存储管理方式都不具有这一特征。因此,我们也可以认为虚拟存储器是具有多次性特征的存储器系统。
  • 对换性。对换性是相对于创痛存储器管理方式的常驻性而言的,是指一个作业中的程序和数据,无须在作业运行时一直常驻内存,而是允许在作业的运行过程中进行换金、换出,亦即,在进程运行期间,允许将那些暂不使用的程序和数据,从内存调至外存的对换区(换出),待以后需要时再将它们从外存调至内存(换进);甚至还允许将暂时不运行的进程调至外存,待它们重又具备运行条件时再调入内存。换进和换出能有效地提高内存利用率。可见,虚拟存储器具有对换性特征,也正是由于这一特征,才使得虚拟存储器得以正常运行。
  • 虚拟性。虚拟性是指能够从逻辑上扩充内存容量,使用户所看到的内存容量远大于实际内存容量。这样,就可以在小的内存中运行大的作业,或者能提高多道程序度。它不仅能有效改善内存的利用率,还可以提高程序执行的并发程度,从而可以增加系统的吞吐量。这是虚拟存储器所表现出来的最重要的特征,也是实现虚拟存储器的最重要的目标。值得说明的是,虚拟性是以多次性和对换性为基础的,或者说,仅当系统允许将作业分多次调入内存,并能将内存中暂时不运行的程序和数据换至盘上时,才有可能实现虚拟存储器;而多次性和对换性又必须建立在离散分配的基础上。

  3.虚拟存储器的实现方法

  在虚拟存储器中,允许将一个作业分多次调入内存。如果采用连续分配方式时,要求必须将作业装入一个连续的内存区域中,则必须事先为它一次性地申请一个足以容纳整个作业的内存空间,以便将整个作业先后分多次装入内存。

  这不仅会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟存储器的实现,都毫无例外地建立在离散分配的存储管理方式的基础上。目前,所有的虚拟存储器都是采用下述方式之一实现的。

(1)请求分页系统

  请求分页系统是在分页系统的基础上,增加了请求调页功能和页面置换功能所形成的页式虚拟存储系统。

  它允许只装入少数页面的程序(及数据),便启动运行。以后,再通过调页功能及页面置换功能,陆续地把即将要运行的页面调入内存,同时把暂不运行的页面换出到外存上。

  置换时以页面为单位。为了能实现请求调页和置换功能,系统必须提供必要的硬件支持和相应的软件。

  • 硬件支持:① 请求分页的页表机制,它是在纯分页的页表机制上增加若干项而形成的,作为请求分页的数据结构;② 缺页中断机构,即每当用户程序要访问的页面尚未调入内存时,便产生一缺页中断,以请求 OS 将所缺的页调入内存;③ 地址变换机构,它同样是在纯分页地址变换机构的基础上发展形成的。
  • 实现请求分页的软件:这里包括有用于实现请求调页的软件和实现页面置换的软件。它们在硬件的支持下,将程序正在运行时所需的页面(尚未在内存中的)调入内存,再将内存中暂时不用的页面从内存置换到磁盘上。

(2)请求分段系统

  请求分段系统是在分段系统的基础上,增加了请求调段及分段置换功能后所形成的段式虚拟存储系统。

  它允许只装入少数段(而非所有的段)的用户程序和数据,即可启动运行。以后再通过调段功能和段的置换功能将暂不运行的段调出,同时调入即将运行的段。

  置换是以段为单位进行的。为了能实现请求调页和置换功能,系统必须提供必要的硬件支持和相应的软件。

  • 硬件支持:① 请求分段的段表机制。这是在纯分段的段表机制基础上增加若干项而形成的,作为请求分段的数据结构。② 缺段中断机构。每当用户程序所要访问的段尚未调入内存时,产生一个缺段中断,以请求 OS 将所缺的段调入内存。③ 地址变换机构。它同样是在纯分段地址变换机构的基础上发展形成的。
  • 软件支持:包括用于实现请求调段的软件和实现段置换的软件。它们在硬件的支持下,先将内存中暂时不用的段从内存置换到磁盘上,再将程序正在运行时所需的段(尚未在内存中的)调入内存。虚拟存储器在实现上是有一定难度的。相对于请求分段系统,因为请求分页系统换进换出的基本单位都是固定大小的页面,所以在实现上要容易些。而请求分段系统换进换出的基本单位是段,其长度是可变的,分段的分配类似于动态分区方式,它在内存分配和回收上都比较复杂。

 

  二、请求分页存储管理方式

  1.请求分页中的硬件支持

  (1)请求页面机制

    在请求分页系统中需要的主要数据结构是请求页表,其基本作用仍然是将用户地址空间中的逻辑地址映射成内存空间中的物理地址。

    为了满足页面换进换出的需要,在请求页表中增加了四个字段,即请求分页系统中每个页表应该包含六个字段:即页号、物理块号、状态位P,访问字段A,修改位M,外存地址。

  • 状态位 P:由于在请求分页系统中,只将应用程序的一部分调入内存,还有一部分仍在外存磁盘上,故在页表中增加一个存在位字段用于指示该页是否已调入内存,供程序访问时参考。
  • 访问字段 A:用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,提供给置换算法(程序)在选择换出页面时参考。
  • 修改位 M:标识该页在调入内存后是否被修改过。由于内存中的每一页都在外存上保留一份副本,因此,在置换该页时,若未被修改,就不需要再将该页回写到外存上,以减少系统的开销和启动磁盘的次数;若已被修改,则必须将该页重写到外存上,以保证外存中所保留的副本始终是最新的。简而言之,M 位供置换页面时参考。
  • 外存地址:用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。

  (2)缺页中断机构

  在请求分页系统中,每当所要访问的页面不存在时,边产生一缺页中断,请求 OS 将所缺的页调入内存。缺页中断作为中断,它们同样需要经历诸如保护 CPU 环境、分析中断原因、转入缺页中断处理程序进行处理,以及在中断处理完成后再恢复 CPU 环境等几个步骤。

  缺页中断又是一种特殊的中断,它与一般的中断相比,有着明显的区别,主要表现在下面两个方面:

  • 在指令执行期间产生和处理中断信号。通常,CPU 都是在一条指令执行完后,才检查是否有中断请求到达。若有,便去响应,否则,继续执行下一条指令。然而,缺页中断是在指令执行期间,发现所要访问的指令或数据不在内存时,便立即所产生和处理缺页中断信号,以便能及时地将所缺的页调入内存。
  • 一条指令在执行期间,可能产生多次缺页中断。基于这些特征,系统中的硬件机构应能保存多次中断时的状态,并保证最后能返回到中断前产生缺页中断的指令处继续执行。

  (3)地址变换机构

  请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,再为实现虚拟存储器而增加了某些功能而形成的,如产生和处理缺页中断,以及从内存中换出一页的功能等等。

  在进行地址变换时,首先去检索快表,试图从中找出所要访问的页。若找到,便修改页表项中的访问位,供置换算法选换出页面时参考。对于写指令,还须将修改位置成“1”,表示该页在调入内存后已被修改。然后利用页表项中给出的物理块号和页内地址形成物理地址。地址变换过程到此结束。

  

  如果在快表中未找到该页的页表项时,应到内存中去查找页表,再从找到的页表项中的状态位 P,来了解该页是否已调入内存。若该页已调入内存,这时应将此页的页表项写入快表,当快表已满时,应先调出按某种算法所确定的页的页表项,然后再写入该页的页表项;若该页尚未调入内存,这时应产生缺页中断,请求 OS 从外存把该页调入内存。

  2.请求分页中的内存分配

  在为进程分配内存时,将涉及到三个问题:

  • 为保证进程能正常运行,所需要的最小物理块的确定
  • 在为每个进程分配物理块时,应采取什么样的分配策略,即所分配的物理块时固定的,还是可变的
  • 为不同进程所分配的物理块数,是采取平均分配算法,还是根据进程的大小按比例分配

  (1)最小物理块数的确定

  一个显而易见的事实是,随着为每个进程所分配的物理块的减少,将使进程在执行中的缺页率上升,从而会降低进程的执行速度。为使进程能有效地工作,应为它分配一定数目的物理块,但这并不是最小物理块数的概念。

  这里所说的最小物理块数,是指能保证进程正常运行所需的最小物理块数。当系统为进程分配的物理块数少于此值时,进程将无法运行。

  至于进程应获得的最少物理块数,与计算机的硬件结构有关,取决于指令的格式、功能和寻址方式。对于某些简单的机器,若是单地址指令且采用直接寻址方式,则所需的最少物理块数为 2。其中,一块是用于存放指令的页面,另一块则是用于存放数据的页面。如果该机器允许间接寻址时,则至少要求有三个物理块。对于某些功能较强的机器,其指令长度可能是两个或多于两个字节,因而其指令本身有可能跨两个页面,且源地址和目标地址所涉及的区域也都可能跨两个页面。对于这种机器,至少要为每个进程分配 6 个物理块,以装入 6 个页面。

  (2)内存分配策略

在请求分页系统中,可采取两种内存分配策略,即固定和可变分配策略。在进行置换时,也可采取两种策略,即全局置换和局部置换。于是可组合出以下三种适用的策略。

  • 固定分配局部置换(Fixed Allocation,Local Replacement)

  固定分配,是指为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。

  局部置换,是指如果进程在运行中发现缺页,则只能从分配给该进程的 n 个页面中选出一个页换出,然后再调入一页,以保证分配给该进程的内存空间不变。

采用该策略时,为每个进程分配多少物理块时根据进程类型(交互性或批处理型等)或根据程序员、程序管理员的建议来确定的。

实现这种策略的困难在于:应为每个进程分配多少个物理块难以确定。若太少,会频繁地出现缺页中断,降低了系统的吞吐量;若太多,又必然使内存中驻留的进程数目减少,进而可能造成 CPU 空闲或其它资源空闲的情况,而且在实现进程对换时,会花费更多的时间。

  • 可变分配全局置换(Variable Allocation,Global Replacement)

  可变分配,是指先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。

  全局置换,是指如果进程在运行中发现缺页,则将 OS 所保留的空闲物理块(一般组织为一个空闲物理块序列)取出一块分配给该进程,或者在所有进程的全部物理块中选择一块换出,然后将所缺的页装入其中。这样, 分配给该进程的内存空间就随之增加。

  可变分配全局置换是最易于实现的一种物理块分配和置换策略,已用于若干个 OS 中。在采用这种策略时,凡产生缺页(中断)的进程,都将获得新的物理块。仅当空闲物理块队列中的物理块用完时,OS 才能从内存中选择一页调出,该页可能是系统中任一进程的页,这样,自然又会使那个进程的物理块减少,进而使其缺页率增加。

  • 可变分配局部置换(Variable Allocation,Local Replacement)

  该策略同样是基于进程的类型或根据程序员的要求,为每个进程分配一定数目的物理块,但当某进程发现缺页时,只允许从该进程在内存的页面中选出一页换出,这样就不会影响其它进程的运行。如果进程在运行中频繁地发生缺页中断,则系统须再为该进程分配若干附加的物理块,直至该进程的缺页率减少到适当程度为止;反之,若一个进程在运行过程中的缺页率特别低,则此时可适当减少分配给该进程的物理块数,但不应引起其缺页率的明显增加。

  (3)物理块分配策略

  在采用固定分配策略时,如何将系统中可供分配的所有物理块分配给各个进程,可采用下述几种算法。

  • 平均分配算法

  这是将系统中所有可供分配的物理块平均分配给各个进程。例如,当系统中有 100 个物理块,有 5 个进程在运行时,每个进程可分得 20 个物理块。这种方式貌似公平,但实际上是不公平的,因为它未考虑到各进程本身的大小。如有一个进程其大小为 200 页,只分配给它 20 个块,这样,它必然会有很高的缺页率;而另一个进程只有 10 页,却有 10 个物理块闲置未用。

  • 按比例分配算法

  这是根据进程的大小按比例分配物理块的算法。如果系统中共有 n 个进程,每个进程的页面数为 Si ,则系统中各进程页面数的总和为:S=(S1+S2+...+Sn),又假定系统中可用的物理块总数为 m,则每个进程所能分到的物理块数为 bi ,则bi = (Si/S) × m, bi 应该取整,它必须大于最小物理块数。

  • 考虑优先权的分配算法

  在实际应用中,为了照顾到重要的、紧迫的作业能尽快地完成,应为它分配较多的内存空间。通常采取的方法是把内存中可供分配的所有物理块分成两部分:一部分按比例地分配给各进程;另一部分则根据各进程的优先权,适当地增加其相应份额后,分配给各进程。在有的系统中,如重要的实时控制系统,则可能是完全按优先权来为各进程分配其物理块的。

  3.页面调入策略

  为使进程能够正常运行,必须事先将要执行的那部分程序和数据所在的页面调入内存,需要解决三个问题:

  • 系统应在何时调入所需页面
  • 系统应从何处调入这些页面
  • 系统是如何调入的

  (1)何时调入页面

    为了确定系统将进程运行时所缺的页面调入内存的时机,可采取预调页策略或请求调页策略:

  • 预调页策略。如果进程的许多页是存放在外存的一个连续区域中,则一次调入若干个相邻的页,会比一次调入一页更高效些。但如果调入的一批页面中的大多数都未被访问,则又是低效的。可采用一种以预测为基础的预调页策略,将那些预计在不久之后便会被访问的页面预先调入内存。如果预测较准确,那么,这种策略显然是很有吸引力的。但遗憾的是,目前预调页的成功率仅约 50%。故这种策略主要用于进程的首次调入时,由程序员指出应该先调入哪些页。
  • 请求调页策略。当进程在运行中需要访问某部分程序和数据时,若发现其所在的页面不在内存,便立即提出请求,由 OS 将其所需页面调入内存。由请求调页策略所确定调入的页,是一定会被访问的,再加之请求调页策略比较易于实现,故在目前的虚拟存储器中大多采用此策略。但这种策略每次仅调入一页,故须花费较大的系统开销,增加了磁盘 I/O 的启动频率。

  (2)从何处调入页面

  在请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。通常,由于对换区是采用连续分配方式,而文件区是采用离散分配方式,故对换区的磁盘 I/O 速度比文件区的高。这样,每当发生缺页请求时,系统应从何处将缺页调入内存,可分成如下三种情况:

  • 系统拥有足够的对换区空间,这时可以全部从对换区调入所需页面,以提高调页速度。为此,在进程运行前,便须将与该进程有关的文件从文件区拷贝到对换区。
  • 系统缺少足够的对换区空间,这时凡是不会被修改的文件都直接从文件区调入;而当换出这些页面时,由于它们未被修改而不必再将它们换出,以后再调入时,仍从文件区直接调入。但对于那些可能被修改的部分,在将它们换出时,便须调到对换区,以后需要时,再从对换区调入。
  • UNIX 方式。由于与进程有关的文件都放在文件区,故凡是未运行过的页面,都应从文件区调入。而对于曾经运行过但又被换出的页面,由于是被放在对换区,因此在下次调入时,应从对换区调入。由于 UNIX 系统允许页面共享,因此,某进程所请求的页面有可能已被其它进程调入内存,此时也就无须再从对换区调入。

  (3)页面调入过程

  • 每当程序所要访问的页面未在内存时(存在位为“0”),便向 CPU 发出一缺页中断,中断处理程序首先保留 CPU 环境,分析中断原因后转入缺页中断处理程序。
  • 该程序通过查找页表,得到该页在外存的物理块后,如果此时内存能容纳新页,则启动磁盘 I/O 将所缺之页调入内存,然后修改页表。
  • 如果内存已满,则须先按照某种置换算法从内存中选出一页准备换出;如果该页未被修改过(修改位为“0”),可不必将该页写回磁盘;
  • 但如果此页已被修改,则必须将它写回磁盘,然后再把所缺的页调入内存,并修改页表中的相应表项,置其存在位为“1”,并将此页表项写入快表中。
  • 在缺页调入内存后,利用修改后的页表,去形成所要访问数据的物理地址,再去访问内存数据。整个页面的调入过程对用户是透明的。

 

  三、页面置换算法

  在进程运行过程中,若其所要访问的页面不在内存而需把它们调入内存,但内存已无空闲空间时,为了保证该进程能正常运行,系统必须从内存中调出一页程序或数据送磁盘的对换区中。

  但应将哪个页面调出,须根据一定的算法来确定。通常,把选择换出页面的算法称为页面置换算法(Page-Replacement Algorithms)。置换算法的好坏,将直接影响到系统的性能。

  不适当的算法可能会导致进程发生“抖动”,即刚被换出的页很快又要被访问,需要将它重新调入,此时又需要再选一页调出;而这个刚被调出的页很快又被访问,又需将它调入,如此频繁地更换页面,以致一个进程在运行中把大部分时间都花费在页面置换工作上,称该进程发生了“抖动”。

   一个好的页面置换算法应具有较低的页面更换频率。从理论上来讲,应将那些以后不再会访问的页面换出,或把那些在较长时间内不会再访问的页面调出。

  1.最佳置换算法和先进先出置换算法

  (1)最佳置换算法

  最佳置换算法中,其所选择的被淘汰页面将是以后永不使用的,或许是在最长(未来)时间内不再被访问的页面。采用最佳置换算法通常可保证获得最低的缺页率。但由于人们目前还无法预知,一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,因而该算法是无法实现的,但可以利用该算法去评价其他算法。

以7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 为例:
77
07 0
17 0 1
22 0 102 0 1 
32 0 302 0 3
42 4 322 4 3
32 4 3
02 0 332 0 3
22 0 3
12 0 122 0 1
02 0 1
12 0 1
77 0 107 0 1
17 0 1

最佳置换算法
最佳置换算法(发生了 6 次页面置换)

  (2)先进先出(FIFO)页面置换算法

  FIFO 算法是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面按先后次序连接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。

  但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。

以7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 为例:
77
07 0
17 0 1
22 0 102 0 1 
32 3 102 3 044 3 024 2 034 2 300 2 330 2 3
20 2 3
10 1 320 1 200 1 2  
10 1 2
77 1 207 0 217 0 1  √

最佳置换算法
FIFO 置换算法(发生了 12 次页面置换)

  2.最近最久未使用和最少使用置换算法

  (1)LRU(Least Recently Used)置换算法

    FIFO 置换算法的性能之所以较差,是因为它所依据的条件是各个页面调入内存的时间,而页面调入的先后并不能反映页面的使用情况。

    最近最久未使用(LRU)页面置换算法是根据页面调入内存后的使用情况作出决策的。由于无法预测各页面将来的使用情况,只能利用“最近的过去”作为“最近的将来”的近似。

  因此,LRU 置换算法是选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问依赖所经历的时间 t。当需淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最久未使用的页面予以淘汰。

以7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 为例:
77(0)
07(1) 0(0)
17(2) 0(1) 1(0)
27(3) 0(2) 1(1)  → 2(0) 0(2) 1(1)  √
02(1) 0(0) 1(2)
32(2) 0(1) 1(3)  → 2(2) 0(1) 3(0)  √
02(3) 0(0) 3(1)
42(4) 0(1) 3(2)  → 4(0) 0(1) 3(2)  √
24(1) 0(2) 3(3)  → 4(1) 0(2) 2(0)  √
34(2) 0(3) 2(1)  → 4(2) 3(0) 2(1)  √
04(3) 3(1) 2(2)  → 0(0) 3(1) 2(2)  √
30(1) 3(0) 2(3)
20(2) 3(1) 2(0)
10(3) 3(2) 2(1)  → 1(0) 3(2) 2(1)  √
21(1) 3(3) 2(0)
01(2) 3(4) 2(1)  → 1(2) 0(0) 2(1)  √
11(0) 0(1) 2(2)
71(1) 0(2) 2(3)  → 1(1) 0(2) 7(0)  √
01(2) 0(0) 7(1) 
11(0) 0(1) 7(2) 
LRU 置换算法(发生了 9 次页面置换)

  可以看到,LRU 置换算法虽然是一种比较好的算法,但要求系统有较多的硬件支持。即必须有寄存器和栈两类硬件中的其中一个的支持。

  • 寄存器。为了记录某进程在内存中各页的使用情况,须为每个在内存中的页面配置一个移位寄存器。当进程访问某物理块时,要将相应寄存器的位置为 1。此时,定时信号将每个一定时间将寄存器右移一位。如果我们把 n 为寄存器的数看成一个整数,那么,具有最小数值的寄存器所对应的页面,就是最近最久未使用的页面。
  • 栈。利用一个栈保存当前使用的各个页面的页面号,每当进程访问某页面时,便将该页面的页面号从栈中移出,将它压入栈顶。因此,栈顶始终是最新被访问页面的编号,而栈底则是最近最久未使用页面的页面号。

  (2)LFU(Least Frequently Used)置换算法

   在采用 LFU 算法时,应为在内存中的每个页面设置一个移位寄存器,用来记录该页面被访问的频率。该置换算法选择在最近时期使用最少的页面作为淘汰页。

  由于存储器具有较高的访问速度,例如 100 ns,在 1ms 时间内可能对某页面连续访问成千上万次,因此,直接利用计数器来记录被访问的次数是不现实的,只能采用较大的时间间隔来记录对存储器某页的访问。

  在最少使用置换算法中采用了移位寄存器的方式,每次访问某页时,便将该移位寄存器的最高位置 1,再每隔一定时间(例如100 ms)右移一次。这样,在最近一段时间使用最少的页将会是Rn-1 + Rn-2 + Rn-3 + ... + R0最小的页。

  LFU 置换算法的页面访问图与 LRU 置换算法的访问图完全相同;或者说,可以利用这样一套硬件即可实现 LRU 算法,又可实现 LFU 算法。

  应该指出,这种算法并不能真正反映出页面的使用情况,因为在每一时间间隔内,只是用寄存器的一位来记录页的使用情况,因此,在该时间间隔内,对某页访问一次和访问 1000 次是完全等效的。

  3.Clock 置换算法

  LRU 算法是较好的一种算法,但由于它要求有较多的硬件支持,故在实际应用中,大多采用 LRU 的近似算法。Clock 算法就是用得较多的一种 LRU 近似算法。

(1)简单的 Clock 置换算法
  当采用简单 Clock 算法时,只需为每页设置一位访问位,再将内存中的所有页面都通过链接指针链接成一个循环队列。当某页被访问时,其访问位被置 1。置换算法在选择一页淘汰时,只需检查页的访问位。如果是 0,就选择该页换出;若为 1,则重新将它置 0,暂不换出,而给该页第二次驻留内存的机会,再按照 FIFO 算法检查下一个页面。当检查到队列中的最后一个页面时,若其访问位仍为 1,则再返回到队首去检查第一个页面。由于该算法是循环地检查各页面的使用情况,故称为 Clock 算法。但因该算法只有一位访问位,只能用它表示该页是否已经使用过,而置换时是将未使用过的页面换出去,故又把该算法称为最近未用算法 NRU(Not Recently Used)。

  

  (2)改进 Clock 置换算法

  在将一个页面换出时,如果该页已被修改过,便须将该页重新写回到磁盘上;但如果该页未被修改过,则不必将它拷回磁盘。在改进型 Clock 算法中,除须考虑页面的使用情况外,还须再增加一个因素,即置换代价,这样,选择页面换出时,既要是未使用过的页面,又要是未被修改过的页面。把同时满足这两个条件的页面作为首选淘汰的页面。由访问位 A和修改位 M 可以组合成下面四种类型的页面:

  • 1 类(A=0,M=0):表示该页最近既未被访问,又未被修改,是最佳淘汰页。
  • 2 类(A=0,M=1):表示该页最近未被访问,但已被修改,并不是很好的淘汰页。
  • 3 类(A=1,M=0):表示该页最近已被访问,但未被修改,该页有可能再被访问。
  • 4 类(A=1,M=1):表示该页最近已被访问且被修改,该页可能再被访问。

  在内存中的每个页必定是这四类页面之一,在进行页面置换时,可采用与简单 Clock算法相类似的算法,其差别在于该算法须同时检查访问位与修改位,以确定该页是四类页面中的哪一种。其执行过程可分成以下三步:① 从指针所指示的当前位置开始,扫描循环队列,寻找 A=0 且 M=0 的第一类页面,将所遇到的第一个页面作为所选中的淘汰页。在第一次扫描期间不改变访问位 A。② 如果第 ① 步失败,即查找一周后未遇到第一类页面,则开始第二轮扫描,寻找 A=0且 M=1 的第二类页面,将所遇到的第一个这类页面作为淘汰页。在第二轮扫描期间,将所有扫描过的页面的访问位都置 0。③ 如果第二步也失败,亦即未找到第二类页面,则将指针返回到开始的位置,并将所有的访问位复 0。然后重复第一步,如果仍失败,必要时再重复第二步,此时就一定能找到被淘汰的页。
  该算法与简单 Clock 算法比较,可减少磁盘的 I/O 操作次数。但为了找到一个可置换的页,可能须经过几轮扫描。换言之,实现该算法本身的开销将有所增加。

  4.页面缓冲算法(Page Buffering Algorithm,PBA)

  虽然 LRU 和 Clock 置换算法都比 FIFO 算法好,但它们都需要一定的硬件支持,并需付出较多的开销,而且,置换一个已修改的页比置换未修改页的开销要大。

  而页面缓冲算法(PBA)则既可改善分页系统的性能,又可采用一种较简单的置换策略。VAX/VMS 操作系统便是使用页面缓冲算法。它采用了前述的可变分配和局部置换方式,置换算法采用的是 FIFO。该算法规定将一个被淘汰的页放入两个链表中的一个,即如果页面未被修改,就将它直接放入空闲链表中;否则,便放入已修改页面的链表中。须注意的是,这时页面在内存中并不做物理上的移动,而只是将页表中的表项移到上述两个链表之一中。

  空闲页面链表,实际上是一个空闲物理块链表,其中的每个物理块都是空闲的,因此,可在其中装入程序或数据。当需要读入一个页面时,便可利用空闲物理块链表中的第一个物理块来装入该页。当有一个未被修改的页要换出时,实际上并不将它换出内存,而是把该未被修改的页所在的物理块挂在自由页链表的末尾。类似地,在置换一个已修改的页面时,也将其所在的物理块挂在修改页面链表的末尾。利用这种方式可使已被修改的页面和未被修改的页面都仍然保留在内存中。当该进程以后再次访问这些页面时,只需花费较小的开销,使这些页面又返回到该进程的驻留集中。当被修改的页面数目达到一定值时,例如 64 个页面,再将它们一起写回到磁盘上,从而显著地减少了磁盘 I/O 的操作次数。一个较简单的页面缓冲算法已在 MACH 操作系统中实现了,只是它没有区分已修改页面和未修改页面。

 

  四、请求分段存储管理方式

   在分页基础上建立的请求分页式虚拟存储器系统,是以页面为单位进行换入、换出的。

  而在分段基础上建立的请求分段式虚拟存储器系统,则是以分段为单位进行换入、换出的。

  它们在实现原理以及所需要的硬件支持上都是十分相似的。在请求分段系统中,程序运行之前,只需调入少数几个分段(不必调入所有的分段)便可启动运行。

  当所访问的段不在内存中时,可请求 OS 将所缺的段调入内存。像请求分页系统一样,为实现请求分段存储管理方式,同样需要一定的硬件和相应的软件提供支持。

  1.请求分段中的硬件支持

  为了实现请求分段式存储管理,应在系统中配置多种硬件机构,以支持快速地完成请求分段功能。与请求分页系统相似,在请求分段系统中所需的硬件支持有段表机制、缺段中断机构和地址变换机构。

  (1)请求段表机制

  在请求分段式管理中所需的主要数据结构是请求段表。由于在应用程序的许多段中,只有一部分段装入内存,其余的一些段仍留在外存上,故须在段表中增加若干项,以供程序在调进、调出时参考。

  

在段表项中,除了段名(号)、段长、段在内存中的起始地址外,还增加了以下诸项。

  • 存取方式:由于段是信息的逻辑单位,可根据该信息的属性对它实施保护,故在段表中增加了存取方式字段,如果该字段为两位,则存取属性是只执行、只读,还是允许读/写。
  • 访问字段 A:其含义与请求分页的相应字段相同,用于记录该段被访问的频繁程度。提供给置换算法选择换出页面时参考。
  • 修改位 M:用于表示该页在进入内存后是否已被修改过,供置换页面时参考。
  • 存在位 P:指示本段是否已调入内存,供程序访问时参考。
  • 增补位:这是请求分段式管理中所特有的字段,用于表示本段在运行过程中是否做过动态增长。
  • 外存始址:指示本段在外存中的起始地址,即起始盘块号。

  (2)缺段中断机构

  在请求分段系统中,每当发现运行进程所要访问的段尚未调入内存时,便由缺段中断机构产生一缺段中断信号,进入 OS 后由缺段中断处理程序将所需的段调入内存。缺段中断机构与缺页中断机构类似,它同样需要在一条指令的执行期间,产生和处理中断,以及在一条指令执行期间,可能产生多次缺段中断。但由于分段是信息的逻辑单位,因而不可能出现一条指令被分割在两个分段中,和一组信息被分割在两个分段中的情况。由于段不是定长的,这使对缺段中断的处理要比对缺页中断的处理复杂。

  

  (3)地址变换机构

  请求分段系统中的地址变换机构是在分段系统地址变换机构的基础上形成的。因为被访问的段并非全在内存,所以在地址变换时,若发现所要访问的段不在内存,必须先将所缺的段调入内存,并修改段表,然后才能再利用段表进行地址变换。为此,在地址变换机构中又增加了某些功能,如缺段中断的请求及处理等。

  

  2.分段的共享与保护

  分段存储管理方式便于实现分段的共享与保护。

  (1)共享段表

  为了实现分段共享,可在系统中配置一张共享段表,所有各共享段都在共享段表中占有一表项。

  在表项的上面记录了共享段的段号、段长、内存始址、存在位等信息,并记录了共享此分段的每个进程的情况。

  

  • 共享进程计数 count。非共享段仅为一个进程所需要。当进程不再需要该段时,可立即释放该段,并由系统回收该段所占用的空间。而共享段是为多个进程所需要的,当某进程不再需要而释放它时,系统并不回收该段所占内存区,仅当所有共享该段的进程全都不再需要它时,才由系统回收该段所占内存区。为了记录有多少个进程需要共享该分段,特设置了一个整型变量 count。当某进程不再需要而释放它时,系统并不立即回收该段所占内存区,而是检查 count 是否为 0,若不是 0,则表示还有进程需要它,仅当所有共享该段的进程全都不再需要它时,此时 count 为 0,才由系统回收该段所占内存区。
  • 存取控制字段。对于一个共享段,应给不同的进程以不同的存取权限。例如,对于文件主,通常允许他读和写;而对其它进程,则可能只允许读,甚至只允许执行。
  • 段号。对于一个共享段,在不同的进程中可以具有不同的段好,每个进程可用自己进程的段号去访问该共享段。

  (2)共享段的分配与回收

  • 共享段的分配。由于共享段是供多个进程所共享的,因此,对共享段的内存分配方法与非共享段的内存分配方法有所不同。在为共享段分配内存时,对第一个请求使用该共享段的进程,由系统为该共享段分配一物理区,再把共享段调入该区,同时将该区的始址填入请求进程的段表的相应项中,还须在共享段表中增加一表项,填写有关数据,把 count 置为 1;之后,当又有其它进程需要调用该共享段时,由于该共享段已被调入内存,故此时无须再为该段分配内存,而只需在调用进程的段表中增加一表项,填写该共享段的物理地址;在共享段的段表中,填上调用进程的进程名、存取控制等,再执行 count = count+1 操作,以表明有两个进程共享该段。以后,凡有进程需要访问此共享段的,都按上述方式在共享段的段表中增加一个表项。
  • 共享段的回收。当共享此段的某进程不再需要该段时,应将该段释放,包括撤消在该进程段表中共享段所对应的表项,以及执行 count = count-1 操作。若结果为 0,则须由系统回收该共享段的物理内存,以及取消在共享段表中该段所对应的表项,表明此时已没有进程使用该段;否则(减 1 结果不为 0),只是取消调用者进程在共享段表中的有关记录。

  (3)分段保护

    在分段系统中,由于每个分段在逻辑上是独立的,因而比较容易实现信息保护。目前,常采用以下几种措施来确保信息的安全。

  • 越界检查。越界检查是利用地址变换机构来完成的。为此,在地址变换机构中设置了段表寄存器,用于存放段表始址和段表长度信息。在进行地址变换时,首先将逻辑地址空间的段号与段表长度进行比较,如果段号等于或大于段表长度,将发出地址越界中断信号;此外,还在段表中为每个设置有段长字段,在进行地址转换时,还要检查段内地址是否等于或大于段长,若大于段长,将产生地址越界中断信号,从而保证了每个进程只能在自己的地址空间内运行。
  • 存取控制检查。存储控制检查是以段为基本单位进行的。为此,在段表的每个表项中,都设置了一个“存取控制”字段,用于规定对该段的访问方式。通常的访问方式有:① 只读,即只允许进程对该段中的程序或数据进行读访问。② 只执行,即只允许进程调用该段去执行,但不准读该段的内容,也不允许对该段执行写操作。③ 读/写,即允许进程对该段进行读/写访问。对于共享段而言,存取控制就显得尤为重要,因而对不同的进程,应赋予不同的读写权限。这时,既要保证信息的安全性,又要满足运行需要。值得一提的是,这里所介绍的存取控制检查是基于硬件实现的,它能较好地保证信息的安全,因为攻击者很难对存储控制字段进行修改。
  • 环保护机制。这是一种功能较完善的保护机制。在该机制中规定:低编号的环具有高优先权。OS 核心处于 0 环内;某些重要的实用程序和操作系统服务占居中间环;而一般的应用程序则被安排在外环上。在环系统中,程序的访问和调用应遵循以下规则:① 一个程序可以访问驻留在相同环或较低特权环中的数据。② 一个程序可以调用驻留在相同环或较高特权环中的服务。
  •  

posted @ 2018-10-19 11:37  BigJunOba  阅读(2405)  评论(0编辑  收藏  举报