清华大学操作系统公开课(五)页面置换算法

  下面介绍在虚拟存储管理中有哪些页面置换算法。

1.总览


局部页面置换算法

  •   最优页面置换算法(OPT,optimal)
  •   先进先出算法(FIFO)
  •   最近最久未使用算法(LRU,Least Recently Used)
  •   时钟页面置换算法(Clock)
  •   最不常用算法(LFU,Least Frequently Used)
  •   Belady现象
  •   LRU、FIFO和Clock的比较

全局页面置换算法

  •   工作集模型
  •   工作集页置换算法
  •   缺页率置换算法

2.页面置换算法


功能

  当缺页中断发生,需要调入新的页面而内存已满时,选择内存当中哪个物理页面被置换。

目标

  尽可能地减少页面的换进换出次数(即缺页中断的次数)。具体来说,把未来不再使用的或短期内较少使用的页面换出,通常只能在局部性原理指导下依据过去的统计数据来进行预测。

页面锁定(frame locking)

  页面锁定技术是用来锁定物理内存中不应该被换出的内存数据。用于描述必须常驻内存的操作系统的关键部分或时间关键(time-critical)的应用进程。实现的方法是:在页表中添加锁定标志位(lock bit)。

  我们该如何评估不同页面置换算法的优劣?通过程序运行时的效率来比较是不容易实现的,可以记录下一个进程对页面的访问轨迹,然后模拟一个页面置换的行为并且记录产生页缺失的数量,以此比较优劣。

3.最优页面置换算法


基本思路

  当一个缺页中断发生时,对于保存在内存当中的每一个逻辑页面,计算在它下一次访问之前,还需要等待多长时间,从中选择等待时间最长的那个,作为被置换的页面。

  这只是一种理想情况,在实际系统中是无法实现的,因为操作系统无从知道每一个页面要等待多长时间后才会再次被访问。

  可用作其它算法的性能评价的依据(在一个模拟器上运行某个城西,并记录每一次的页面访问情况,在第二遍运行时即可使用最优算法)。

示例

  物理内存大小为4个帧,刚开始存入的物理帧是a、b、c、d,一共需要载入5个帧,当访问e时发生缺页中断,此时置换入e,置换出d,因为下一次访问d的等待时间最长,暂时用不到d,可以让d待在外存。

4.先入先出算法(First-In First-Out,FIFO)


基本思路

  选择在内存中驻留时间最长的页面并淘汰之。具体来说,系统维护着一个链表,记录了所有位于内存中的逻辑页面。从链表的排序顺序来看,链首页面的驻留时间最长,链尾页面的驻留时间最短。当发生一个缺页中断时,把链首页面淘汰出局,并把新的页面添加到链表的末尾。

  性能较差,调出的页面有可能是经常要访问的页面,并且有Belady现象。FIFO算法很少单独使用。

示例

  FIFO算法实现简单,一个单一指针足够,但是缺页中断比别的算法可能要多。比如在时间5时,换入e就换出链表头的a,当7时又要换入a,换出此时的链表头的b。

5.最近最久未使用算法(Least Recently Used,LRU)


基本思路

  当一个缺页中断发生时,选择最久未使用的那个页面,并淘汰之。这里所说的最久并非是FIFO说的驻留最久,而是内存页面里过去最久没被使用的页面。它是对最优页面置换算法的一个近似,其依据是程序的局部性原理,即在最近一小段时间(最近几条指令)内,如果某些页面被频繁地访问,那么在将来的一小段时间内,它们还可能会再一次被频繁地访问。反过来说,如果在过去某些页面长时间未被访问,那么在将来它们还可能会长时间得不到访问。

示例

  LRU算法在时间5访问e的时候发生缺页,此时他发现c是最久没被引用过的,上一次被访问是在时间1的时刻,故换出c换入e,后面换入换出的过程也如此。

LRU实现办法

  LRU算法需要记录各个页面使用时间的先后顺序,开销比较大,两种可能的实现方法是:

  •   系统维护一个页面链表,最近刚刚使用过的页面作为首节点,最久未使用的页面作为尾节点。每一次访问内存时,找到响应的页面,把它从链表中摘下来,再移动到链表之首。每次缺页中断发生时,淘汰链表末尾的页面。实现时可用hashmap+链表将查找响应节点的时间复杂度降到O(1)。
  •   设置一个活动页面栈,当访问某页时,将此页号压入栈顶,然后,考察栈内是否有与此页面相同的页号,若有则抽出。当需要淘汰一个页面时,总是选择栈底的页面,它就是最久未使用的。

6.时钟页面置换算法


  我们既想要FIFO那样的高效简单实现,又想要LRU那样较少的缺页率,所以采用一种折中的Clock页面置换算法,是LRU的近似,对FIFO的一种改进。时钟页面置换算法是一种不精确的访LRU算法。

基本思路

  需要用到页表项当中的访问位,当一个页面被装入内存时,把该位初始化为0。然后如果这个页面被访问(读/写),则把该位置为1;

  把各个页面组织成环形链表(类似于钟表面),把指针指向最老的页面(最先进来);

  当发生一个缺页中断时,考察指针所指向的最老页面,若它的访问位为0,立即淘汰;若访问位为1,则把该位置为0,然后指针往下移动一格。如此下去,直到找到被淘汰的页面,然后把指针移动到它的下一格。

  要注意的是,每次访问页表项时,页表项置为1的操作是硬件直接完成的。(我觉得Clock算法中指针移到上边变1为0的操作是软件完成的)。

  基本思路中所说的环形链表、指针、访问位形式大致如图所示。置换的流程要注意以下几点:

  •     访问位置1的操作是由硬件自动完成的:每次访问的时候,硬件会检查页表项的访问位是否为1,如果不是则置为1。
  •     指针只有在发生缺页异常的时候才移动,检查移动到的页访问位是否为1,如果为1则置为0继续扫变页面寻找bit = 0,如果为0则换出。换出以后,指针往后移动一个。

示例

  可以从示例更方便地看出如何运行,前四步访问时未发生缺页中断,硬件自动把a~d访问位置1,时间5访问了e发生缺页中断,指针移动寻找bit=0的页表项,发现第一轮旋转a~d都是1,把它们每一个变成0,第二轮找到a的bit=0,换出a换入e,然后硬件自动置e的访问位为1。如此继续下去,置换的时候注意上面两个要点不难看懂。

7.二次机会置换算法(优化的时钟页面置换算法)


基本思路

  只通过访问位没办法区分写操作和读操作,而只被读过的页换出开销小,可以直接回收内存空间,省去了写回的操作。但被写过的页在内存中的数据和在外存中的数据不一致,回收时需要写回外存,这就增加了额外的开销。页表项中还有一项修改位(dirty bit)标志是否发生写操作,可以利用修改位的信息减少修改页的缺页处理开销。  

  二次机会置换算法中利用访问位和修改位同时指导置换。

示例

  可以看到相较普通的CLOCK时钟置换算法而言,同时考虑访问位和修改位(访问位和修改位的置1操作都是由硬件自动完成的)。然后根据上面提到的利用访问位和修改位同时指导策略来置换页。

  遇到的问题:当页表项的bit为01这种情况(访问位为0,修改位为1),把bit置为00继续指针的移动,那么修改位为0后我们丢失了修改位的信息,怎么知道换出的时候是否要写到内存呢?是修改位在置0的时候就写入到外存吗?那岂不是没有省下写出的那部分开销。

8.最不常用算法(Least Frequently Used,LFU)


基本思路

  当一个缺页中断时,选择访问次数最少的那个页面,并淘汰之。

实现方法

  对每一个页面设置一个访问计数器,每当一个页面被访问时,该页面访问计数器加1。在发生缺页中断时,淘汰计数值最小的那个页面。

 

  LRU和LFU的区别:LRU考察的是多久未访问,时间越短越好;而LFU考察的是访问的次数或频数,访问次数越多越好。

  那么LFU可能存在的问题是:

  •   假如有一些页程序执行前期频繁访问,后期不怎么访问,这些页因为频次高在后期也不会被换出,缺页率因此增加。
  •   LFU的实现需要琢磨:维护一个数组表示每页计数器的空间开销和查找最小计数的页的遍历开销。

  LFU相较LRU其实少了时间的考量,我们可以定期把每页计数器的值除2,这样早起访问多后期不访问的页会随着时间的更替被换出。

示例

 

9.Belady现象


定义

  在采用FIFO算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象。

原因

  FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,与置换算法的目标是不一致的(即替换较少使用的页面),因此,被它置换出去的页面并不一定是进程不会访问的。

示例

  上面示例中,紫色代表发生缺页中断的状态(物理页面空闲区域第一次载入页的过程也是一次缺页中断),我们可以看出FIFO置换算法在物理页面数增加的情况下反而缺页率增加,这就是belady现象。

  而LRU算法没有belady现象,这是因为LRU符合栈算法的特点,栈算法属性就是物理内存越多,缺页越少。

  LRU相较于FIFO更考虑到程序运行局部性的特征。CLOCK时钟置换算法只是利用访问位粗估计时间,也没有很好地体现局部性特征,也不能算是栈算法,实际上是一种FIFO的改进。

LRU、FIFO和Clock的比较

  LRU算法和FIFO本质上都是先进先出的思路,只不过LRU是针对页面的最近访问时间来排序,所以需要在每一次页面访问的时候动态地调整各个页面之间的先后顺序(有一个页面的最近访问时间变了);而FIFO是针对页面进入内存的时间来进行排序,这个时间是固定不变的,所以各个页面之间的先后顺序是固定的。如果一个页面在进入内存后没有被访问,那么它的最近访问时间就是它进入内存的时间。换句话说,如果内存当中的所有页面都未曾访问过,那么LRU算法就退化为FIFO算法。

  LRU算法性能较好,但系统开销较大;FIFO算法系统开销较小,但可能会发生belady现象。因此,折衷的办法是Clock算法,在每一次页面访问时,它不必去动态地调整该页面在链表当中的顺序,而仅仅是做一个标记,然后等到发生缺页中断的时候,再把它移动到链表末尾。对于内存当中那些未被访问过的页面,Clock算法的表现和LRU算法一样好;而对于那些曾经被访问过的页面,它不能像LRU算法那样,记住它们的准确位置。

10.工作集和常驻集


  上面说的页面置换算法都是局部置换算法,局部置换算法的问题在于物理页帧的大小会影响置换算法的性能,操作系统里跑多个程序时,分给每个程序的物理内存大小是动态的,而局部置换算法没有考虑进程访存的差异。

问题

  前面介绍的各种页面置换算法,都是基于一个前提,即程序的局部性原理。但此原理是否成立?

  •     如果局部性原理不成立,那么各种页面置换算法就没有什么区别,也没有什么意义。例如:假设进程对逻辑页面的访问顺序是1、2、3、4、5、6、7、8……,即单调递增,那么在物理页面数有限的前提下,不管采用何种置换算法,每次的页面访问都必然导致缺页中断。
  •   如果局部性原理是成立的,那么如何来证明它的存在,如何来对它进行定量地分析?这就是工作集模型

工作集

  工作集:一个进程当前正在使用的逻辑页面集合。可用一个二元函数W(t,△)来表示。

  t是当前的执行时刻;△称为工作集窗口(working-set window),即一个定长的页面访问的时间窗口;W(t,△)=在当前时刻t之前的△时间窗口内的所有页面所组成的集合(随着t的变化,该集合也在不断地变化);| W(t,△) | 指工作集的大小,即页面数目。

 

  工作集的示例如上图所示。

  工作集大小的变化:进程开始执行后,随着访问新页面逐步建立较稳定的工作集。当内存访问的局部性区域的位置大致稳定时,工作集大小也大致稳定;局部性区域的位置改变时,工作集快速扩张和急速收缩过渡到下一个稳定值。

常驻集

  常驻集是指在当前时刻,进程实际驻留在内存当中的页面集合。

  • 工作集是进程在运行过程中固有的属性,而常驻集取决于系统分配给进程的物理页面数目,以及采取的置换算法;
  • 如果一个进程的整个工作集都在内存当中,即常驻集 ⊇工作集,那么进程将很顺利地运行,而不会造成太多的缺页中断(直到工作集发生剧烈变动,从而过渡到另一个状态);
  • 进程常驻集的大小达到某个数目之后,再给它分配更多的物理页面,缺页率也不会明显下降。

  

11.两个全局置换算法


工作集置换算法

思路

  换出不在工作集的页面,窗口大小τ是当前时刻前t个内存访问的页是工作集,τ被称为窗口大小。

实现方法

  •   访存链表:维护窗口内的访存页面链表
  •   访存时,换出不在工作集的页面:更新访存链表
  •   缺页时,换入页面:更新访存链表

示例

  这里要注意工作集置换算法的特点是即使没有中断,也可能会有换出的操作。示例中,工作集的窗口被设定为τ=4。刚开始在t=-2、t=-1、t=0时刻分别访问了e、d、a;在t=1时访问c,没有c所以产生缺页中断添加进内存,此时工作集窗口刚好覆盖t=-2 ~ 1 的size=4的窗口,所以不产生淘汰;t=2时访问c,c在内存中直接访问,此时工作集窗口覆盖t=-1 ~ 2,而e的访问在t=-2时,所以换出e(没有中断也会有换出操作);在看t=4时访问b,产生缺页中断添加b,此时工作集窗口覆盖t=1~4,d的上次访问是在t=-1时,换出d;后面依次改变。

  与前面的变化就是,它当前物理页帧中放哪些页,是取决与它是否在工作集窗口之内。这样可以确保我们的物理内存中始终有也存在足够的页帧,给其他运行程序提供更多内存,减少页面置换的次数,这是在整个物理内存层面(多个程序层面)考量的。

缺页率页面置换算法

可变分配策略

  常驻集大小可变。例如:每个进程在刚开始运行的时候,先根据程序大小给它分配一定数目的物理页面,然后在进程运行的过程中,再动态地调整常驻集的大小。基于缺页率的置换算法期望的是每个程序的缺页率都比较好。

  •   可采用全局页面置换的方式,当发生一个缺页中断时,被置换的页面可以是在其他进程当中,各个并发进程竞争地使用物理页面。
  •   优缺点:性能较好,但增加了系统开销。
  •   具体实现:可以使用缺页率算法(PFF,page fault frequency)来动态调整常驻集的大小。

缺页率  

  缺页率表示“缺页次数/内存访问次数”(比率)或“缺页的平均时间间隔的倒数”。影响缺页率的因素:

  •   页面置换算法
  •   分配给进程
  •   页面本身的大小
  •   程序的编写方法

缺页率置换算法目的

缺页率置换算法实现

 

示例

  上图示例中每次缺页中断的置换特征是:

  •     如果Tcurrent - Tlast > 2 ,从工作集移除没有在[Tlast,Tcurrent]被引用的页面
  •     如果Tcurrent-Tlast <= 2,仅增加缺失页到工作集   

  我们可以看出t=1时访问c发生缺页中断,此时还没Tlast,就增加缺失页到工作集;在t=4时访问b又发生了缺页中断,Tcurrent=4 and Tlast = 1,Tcurrent-Tlast>2所以移除在t1~4没被引用的a,增加缺失页b到工作集;在t=6时访问e发生了缺页中断,Tcurrent=6 nad Tlast = 4,Tcurrent-Tlast<=2所以仅增加e到工作集。

两种全局页面置换算法的不同

  工作集置换每一次都考虑换入换出,缺页置换算法只有在中断的时候考虑换入换出。这两个算法是通过工作集的大小和缺页率的大小来动态调整整个内存中置换页的情况。

12.抖动


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值