----------虚拟内存的基本概念:
1.传统存储管理方式的特征:
内存管理中的内容都是为了将多个进程保存在内存中以便允许多道程序设计。它们都具有以下两个共同特征:
(1)、一次性:作业必须一次性全部装入内存后,方能开始运行。这会导致两种情况发生:
第一种是:当作业很大,不能全部被装入内存时,将使该作业无法运行;
第二种是:当大量作业要求运行时,由于内存不足以容纳所有作业,只能使少数作业先运行,导致多道程序度的下降
(2)、驻留性:作业被装入内存后,就一直驻留在内存中,其任何部分都不会被换出,直至作业运行结束。运行中的进程,会因为等待 I / O
而被阻塞,可能处于长期等待状态。
由上述分析可知,许多在程序运行中不用或暂时不用的程序(数据)占据了大量的内存空间,而一些需要运行的作业又无法装入运行,显然
浪费了宝贵的内存资源。
2.局部性原理:
要真正理解虚拟内存技术的思想,首先必须了解计算机中著名的局部性原理。著名的 Bill Joy (SUN 公司的 CEO)说过:“在研究所的时候,我经
常开玩笑地说高速缓存是计算机科学中唯一重要的思想。事实上,高速缓冲技术确实极大地影响了计算机系统的设计。”快表、页高速缓存以及虚拟内
存技术从广义上讲,都属于高速缓存技术。 这个技术所依赖的原理就是局部性原理。局部性原理既适用于程序结构,也适用于数据结构(更远地讲,
Dijkstra著名的关于“goto 语句有害”的论文也是处于对程序局部性原理的深刻认识理解)。
局部性原理表现在以下两个方面:
(1)、时间局部性:
如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的
典型原因,是由于程序中存在这大量的循环操作。
(2)、空间局部性:
一旦程序访问了某个存储单元,在不久以后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,
这是因为指令通常是顺序存放、顺序执行的,数据也一定是以向量、数组、表等形式簇聚存储的。
补充:》》时间局部性是通过将近来使用的指令和数据保存到高速缓存存储器中,并使用高速缓存的层次结构实现。
》》空间局部性通常是使用较大的高速缓存,并将预取机制集成到高度缓存控制逻辑中实现。
》》虚拟内存技术实际上就是建立了“内存----外存”的两级存储结构,利用局部性原理实现高速缓存。
3.虚拟存储器的定义和特征:
基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其余部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息
不在内存中时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统内存中暂时不使用的内容换出到外存上,从而腾出空间存
放将要调入内存的信息。这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
之所以将其称为虚拟存储器,是因为这种存储器实际上并不存在,只是由系统提供了部分装入、请求调入和置换功能后(对用户完全透明),给用户
的感觉好像存在一个比实际物理内存大得多的存储器。虚拟存储器的大小由计算机的地址结构决定,并非是内存和外存的简单相加。
虚拟存储器有以下三个主要特征:
(1)、多次性:是指无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行。
(2)、对换性:是指无需在作业运行时一直常驻内存,而是允许作业的运行过程中,进行换进和换出。
(3)、虚拟性:是指从逻辑上扩充内存的容量,使用户所看到的内存容量,远大于实际的内存容量。
4.虚拟内存技术的实现:
虚拟内存中,允许将一个作业分多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,
而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
虚拟内存的实现有以下三种方式:
》》请求分页存储管理
》》请求分段存储管理
》》请求段页式存储管理
不管哪种方式,都需要一定的硬件支持。一般需要的支持有以下几个方面:
》》一定容量的内存和外存
》》页表机制(或段表机制),作为主要的数据结构
》》中断机构,当用户程序要访问的部分尚未调入内存,则产生中断
》》地址变换机构,逻辑地址到物理地址的变换
------------请求分页管理方式:
请求分页系统建立在基本分页系统基础之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器
的方法。
在请求分页系统中,只要求将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存时,再通过调页功能将其
调入,同时还可以通过置换功能将暂时不用的页面换出外存上,以便腾出内存空间。
为了实现请求分页,系统必须提供 一定的硬件支持。除了需要一定容量的内存及外存的计算机系统,还需要有页表机制、缺页中断机构和地址变换机构。
1.页表机制
请求分页系统的页表机制不同于基本分页系统,请求分页系统在一个作业运行之前不要求全部一次性调入内存,因此在作业的运行过程中,必然会出现要访问的
页面不在内存的情况,如何发现和处理这种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了四个字段。如下图:
请求分页系统中的页表项
增加的四个字段说明如下:
》》状态位P :用于指示该页面是否已调入内存,供程序访问时参考
》》访问字段A : 用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面时参考
》》修改位M: 标识该页在调入内存后是否被修改过
》》外存地址:用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考
2.缺页中断机构
在请求分页系统中,每当所要访问的页面不在内存时,便产生一个缺页中断,请求操作系统将所缺的页面调入内存。此时应将缺页的进程阻塞(调页完
成唤醒),如果内存有空闲块,则分配一个块,将要调入的页装入该块,并修改页表中相应的页表项,若此时内存中没有空闲块,则要淘汰某页(若被淘
汰页在内存期间被修改过,则要将其写回外存)。
缺页中断作为中断同样要经历,诸如保护CPU环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有
以下两个明显的区别:
》》在指令执行期间产生和处理中断信号,而非一条指令执行完后,属于内部中断。
》》一条指令在执行期间,可能产生多次缺页中断。
3.地址变换机构
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的。
在进行地址变换时,先检索快表:
》》若找到要访问的页,便修改页表项中的访问位(写指令则还须重置修改位),然后利用页表项中给出的物理块号和页内地址形成物理地址。
》》若未找到该页的页表项,应到内存中区查找页表,再对比页表项中的状态位P ,看该页是否已调入内存,未调入则产生缺页中断,请求从
外存把该页调入内存。
-------------页面置换算法(决定应该换入哪页换出哪页)
进程运行时,若其访问的页面不在内存而需要将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。
而选择调出页面的算法就称为页面置换算法。好的页面置换算法应有较低的页面更换频率,也就是说,应将以后不会再访问或者以后较长时间
内不会再访问的页面先调出。
常见的置换算法有以下四种:
1.最佳置换算法(OPT)
最佳置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再访问的页面,这样可以保证获得最低的缺页率。但由于人们
目前无法预知进程在内存下的若干页面中哪个是未来最长时间不再被访问的,因而该算法无法实现。
最佳置换算法可以用来评价其他算法。
注意:最长时间不被访问和以后被访问次数最小是不同的概念。
2.先进先出(FIFO)页面置换算法
优先淘汰最早进入内存的页面,亦即在内存中驻留时间最久的页面。该算法实现简单,只需要把调入内存的页面根据先后次序链接成队列,设置一个
指针总是指向最早的页面。但该算法与进程实际运行的规律不适应,因为在进程中,有的页面经常被访问。
补充:》》FIFO算法还会产生所分配的物理块数增大而页故障数不减反增的异常现象,这是由 Belady 于 1969 年发现,故称为 Belady 异常。
》》只有FIFO 算法会出现 Belady 异常,而LRU 和 OPT 算法永远不会出现 Belady 异常。
3.最近最久未使用(LRU)置换算法
选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问
字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择有页面中值最大的予以淘汰。
补充:》》实际上,LRU算法根据各页以前的情况,是“ 向前看 ”的,而最佳置换算法则根据各页以后的使用情况,是“ 向后看 ”的。
》》LRU 性能较好,但需要寄存器和栈的硬件支持。LRU是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现Belady 异常。FIFO 算法是
基于队列实现,不是堆栈类算法。
4.时钟(CLOCK)置换算法
LRU算法的性能接近于OPT ,但是实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。所以操作系统的设计者尝试了很多算法,试图用比较
小的开销接近LRU的性能,这类算法都是 CLOCK 算法的变体。因为算法要循环扫描缓冲区,像时钟的针一样转动,所以叫 CLOCK 算法。
简单的CLOCK 算法是给每一帧关联一个附加位,称为使用位。当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用
位也被置为1.对于页面置换算法,用于替换的候选帧集合看做一个循环缓冲区,并且有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲
区 的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用为被值为0 的一帧。每当遇到一个使用位为1的帧时,操作系统将该位重新置为0;如果
在这个过程开始时,缓冲区所有帧的使用位均为 0,则选择遇到的第一个帧替换;如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使
用位都置为0,并且停留在最初的位置上,替换该帧中的页。由于该算法循环地检查各页面的情况,故称为CLOCK 算法,又称为最近未用(NRU)算法。
不要忽略CLOCK算法的另外一个名称,有时题目中就以 NRU 算法的名称给出。
CLOCK算法的性能比较接近 LRU ,而通过增加使用的位数目,可以使得 CLOCK 算法更加高效。在使用位的基础上再增加一个修改位,则得到改进的
CLOCK置换算法。这样,每一帧都处于以下四种情况之一:
(1)、最近未被访问,也未被修改(u = 0 , m = 0)
(2)、最近被访问,但未被修改(u = 1 , m = 0 )
(3)、最近未被访问,但被修改(u = 0 , m = 1)
(4)、最近被访问,被修改(u = 1 , m = 1)
算法执行如下操作步骤:
(1)从指针的当前位置开始,扫描帧缓冲区。在这次扫描过程中,对使用位不做任何修改。选择遇到一的第一个帧(u=0 , m = 0)用于替换。
(2)如果第(1)步失败,则重新扫描,查找(u = 0 , m = 1)的帧。选择遇到的第一个这样的帧用于替换。在这个扫描过程中,对每个跳过的
帧,把它的使用位设置为 0 。
(3)如果第(2)步失败,指针将回到它的最初位置,并且集合中所有帧的使用位均为 0 。重复第 1 步,并且如果有必要,重复第 2 步。这样就
可以找到供替换的帧。
补充:---- 改进的CLOCK算法优于简单CLOCK算法之处在于替换时首选没有变化的页。由于修改过的页在替换之前必须写回,因而这样做会节省时间。
-----有读者 会认为CLOCK算法和改进型CLOCK算法记忆起来不容易,可以这样来总结一下,方便记忆:
操作系统中任何经过优化而有效的页面置换算法都有一个原则,就是尽可能保留曾经使用过的页面,而淘汰未使用的页面,认为这样可以在
总体上减少换页次数。CLOCK算法只考虑到是否给访问过,那么被访问过的当然尽可能留下,未使用过的就淘汰,而改进型CLOCK算法把
使用过的页面又细分了一下,分为使用过但未修改过和使用过而且修改过,所以,如果有未使用过的页面,那么当然首先把它换出,如果
全部页面都使用过,当然优先选择把未修改过的页面换出。
-------------------页面分配策略:
1.驻留集大小
对于分页式的虚拟内存,在准备执行时,不需要也不可能把一个进程的所有页都取到主存,因此,操作系统必须决定读取多少页。也就是说,给特定的
进程分配多大的主存空间,这需要考虑以下几点:
(1)、分配给一个进程的存储量越小,在任何时候驻留在主存中的进程数就越多,从而可以提高处理机的时间利用效率。
(2)、如果一个进程在主存中的页数过少,尽管有局部性原理,页错误率仍然会相对较高。
(3)、如果页数过多,由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响。
基于这些因素,现代操作系统通常采用三种策略:
(1)、固定分配局部置换
它为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中页面中选出一页换出,然后再
调入需要的页面。实现这种策略难以确定为每个进程应分配的物理块数目:太少会频繁出现缺页中断,太多又会使CPU和其他资源利用率下降。
(2)、可变分配全局置换
这是易于实现的物理块分配和置换策略,为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。当某个进程发生缺
页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。这种方法比固定分配局部置换更加灵活,可以动态增加进程
的物理块,但也存在弊端,它盲目地给进程增加物理块,将会导致系统多道程序并发能力下降。
(3)、可变分配局部置换
它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许从该进程的内存的页面中选出一个页换出,这样就不会影响其他进程的运行。
如果进程在运行中频繁地缺页,系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度;反之,若进程在运行中缺页率特别低,则可适当
减少分配给该进程的物理块。比起可变分配全局置换,这种方法不仅可以动态增加进程物理块的数量,还能动态减少进程物理块的数量,在保证进程
不会过多地调页的同时,也保持了系统的多道程序并发能力。当然它需要更复杂的实现,也需要更大的开销,但对比频繁地换入换出所浪费的计算机
资源,这种牺牲是值得的。
注意:所谓“ 局部 ”:仅仅针对当前的进程
所谓“ 全局 ”:针对所有的进程
2.调入页面的时机
为确定系统将进程运行时所缺的页面调入内存的时机,可采取以下两种调页策略:
(1)、预调页策略。根据局部性原理,一次调入若干个相邻的页可能会比一次调入一页更高效。但如果调入的一批页面中大多数都未被访问,则又是
低效的。所以就需要采用预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存。但目前预调页策略的成功率仅约 50% 。故
这种策略主要用于进程的首次调入时,由程序员指出应该先调入哪些页。
(2)、请求调页策略。进程在运行中需要访问的页面不在内存而提出请求,由系统将所需页面调入内存。由这种策略调入的页一定会被访问,且这种
策略比较易于实现,故在目前的虚拟存储器中大多采用此策略。它的缺点在于每次只调入一页,调入调出页面数过多时会花费过多的 I / O 开销。
补充:预调入策略实际上就是运行前的调入,请求调页实际上就是运行期间的调入。一般情况下,两种调页策略会同时使用。
3.从何处调入页面
请求分页系统中的外存分为两部分:用于存放文件的文件区 和 用于存放对换页面的对换区。
对换区通常是采用连续分配方式,而文件区采用离散分配方式,故对换区的磁盘 I / O 速度比文件区的更快。这样从何处调入页面有三种情况:
(1)、系统拥有足够的对换区空间:可以全部从对换区调入所需页面,以提高调页的速度。为此,在进程运行之前,需将与该进程有关的文件从
文件区复制到对换区。
(2)、系统缺少足够的对换区空间:凡不会被修改的文件都直接从文件区调入;而当换出这些页面时,由于它们未被修改而不必再将它们换出。但
对于那些可能被修改的部分,在将它们换出时须调到对换区,以后需要时再从对换区调入(这是因为读的速度比写的速度快)。
(3)、UNIX 方式:与进程有关的文件都放在文件区,故未运行过的页面,都应从文件区调入。曾经运行过但又被换出的页面,由于是被放在对换区,
因此下次调入时应从对换区调入。进程请求的共享页面若被其他进程调入内存,则无需再从对换区调入。
-----------------抖动:
在页面置换过程中的一种最糟糕的情形是,刚刚换出的页面马上又要换入主存,刚刚换入的页面马上就要换出主存,这种频繁的页面调度行为称为抖动,
或颠簸。如果一个进程在换页上用的时间多于执行时间,那么这个进程就在颠簸。
频繁的发生缺页中断(抖动),其主要原因是某个进程频繁访问的页面数目高于可用的物理页帧数目。虚拟内存技术可以在内存中保留更多的进程以
提高系统效率。在稳定状态,几乎主存的所有空间都被进程块占据,处理机和操作系统可以直接访问到尽可能多的进程。但如果管理不当,处理机的大部
分时间都用于交换块,即请求调入页面的操作,而不是执行进程的指令,这就会大大降低系统效率。
---------------工作集:
工作集(或驻留集)是指在某段时间间隔内,进程要访问的页面集合。经常被使用的页面需要在工作集中,而长期不使用的页面要从工作集中被丢弃。
为了防止系统出现抖动现象,需要选择合适的工作集大小。
工作集模型的原理是:让操作系统跟踪每个进程的工作集,并为进程分配大于其工作集的物理块。如果还有空闲物理块,则可以再调入一个进程到内存
以增加多道程序数。如果所有工作集之和增加以至于超过了可用物理块的总数,那么操作系统会暂停一个进程,将其页面调出并且将其物理块分配给其他
进程,防止出现抖动现象。
注意:正确选择工作集的大小,对存储器的利用率和系统吞吐量的提高,都将产生重要影响。
---------------地址翻译:
结合计算机组成原理中的 Cache 进行理解。