页面置换算法详解(10种)

文章目录

  • 1. 最优页面置换算法
  • 2. 最近未使用页面置换算法(NRU)(Not Recently Used)
  • 3. 先进先出页面置换算法(FIFO)
  • 4. 第二次机会页面置换算法(SC)(Second Chance)
  • 5. 时钟页面置换算法(CLOCK)
  • 6. 最近最少使用页面置换算法(LRU)(Least Recently Used)
  • 7. 最不常用页面置换算法(NFU)(Not Frequently Used)
  • 8. 老化算法
  • 9. 工作集页面置换算法
  • 10. 工作集时钟页面置换算法

       如果对于虚拟内存,页表,分页等技术还是一知半解的道友可以参考我之前写的一篇博客: 虚拟内存、分页以及页表,建议读者从头往后读,有的页面置换算法是对前面页面置换算法的修改或者性能提升。
       当发生缺页中断时,操作系统必须在内存中选择一个页面将其换出内存,以便为即将调入的页面腾出空间。

1. 最优页面置换算法

       此算法不可能实现。在发生缺页中断的时候,在内存中的页面有的很快就会被访问,而有的页面可能要到10、100、1000条指令后才会被访问。此时要置换最迟被访问的页面,把因调用被替换的页面而引起的中断推迟到将来,越久越好。当缺页中断发生时,操作系统无法知道各个页面下一次将在什么时候被访问,因此这个算法是无法被实现的。
       这个算法可以用来对其他可实现算法的性能进行比较。

2. 最近未使用页面置换算法(NRU)(Not Recently Used)

       当页面被访问(读或写)时设置R位,页面被写入(修改)时设置M位。
       当启动一个进程时,它的所有页面的两个位都由操作系统设为0,R位被定期地(比如在每次时钟中断时)清零,以区别最近没有被访问的页面和被访问的页面。
       当发生缺页中断时,操作系统检查所有的页面并根据它们当前的R位和M位的值,把它们分为4类:

  • 第0类:没有被访问,没有被修改。
  • 第1类:没有被访问,已被修改(M)。
  • 第2类:已被访问,没有被修改(R)。
  • 第3类:已被访问,已被修改(RM)。

       NRU(Not Recently Used)算法随机地从类编号最小的非空类中挑选一个页面淘汰。在一个时间滴答中(大约20ms)淘汰一个没有被访问的已修改页面要比淘汰一个被频繁使用的“干净”页面好。NRU算法的主要优点是易于理解和能够有效地被实现,虽然它的性能不是最好的,但是已经够用了。

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

       开销同样较小的FIFO算法,最新进入的页面放在表尾,最早进入的页面放在表头。当缺页中断时,淘汰表头的页面并把新调入的页面加到表尾。这种算法的缺点是可能会把有用的页面淘汰掉。

4. 第二次机会页面置换算法(SC)(Second Chance)

       对FIFO算法的改进,对FIFO算法做一个简单的修改:检查最老页面的R位。如果R位是0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是1,就将R位置0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入时间使它就像刚装入的一样,然后继续搜索。
       第二次机会(second chance)算法是寻找一个在最近的时钟间隔内没有被访问过的页面。如果所有的页面都被访问过了,该算法就简化为纯粹的FIFO算法。假设所有页面的R位都被设置了,操作系统将会一个接一个地把每个页面都移动到链表的尾部并清除被移动的页面的R位。最后又会回到原来的表头页面,此时它的R位已经被清除了,因此这个页面会被淘汰,所以这个算法总是可以结束的。

5. 时钟页面置换算法(CLOCK)

       对第二次机会算法的改进,第二次机会算法经常要在链表中移动页面,既降低了效率又不是很必要。
       一个更好的做法就是把所有的页面都保存在一个类似钟面的环形链表中,一个表指针指向最老的页面。当发生缺页中断时,首先检查表指针指向的页面,如果它的R位是0就淘汰该页面,并把新的页面插入这个位置,然后把表指针前移一个位置。如果R位是1就清除R位并把表指针前移一个位置;重复这个过程一直到找到一个R位为0的页面为止。

6. 最近最少使用页面置换算法(LRU)(Least Recently Used)

       在缺页中断发生时,置换未使用时间最长的页面。
       LRU理论上是可以实现的,但是代价很高。维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。困难的是在每次访问内存时都必须要更新整个链表。
       假设用硬件实现:硬件有一个64位计数器C,它在每条指令执行完后自动加1,每个页表项必须有一个足够容纳这个计数器值的域。在每次访问完内存后,将当前的C值保存到被访问页面的页表项中。一旦发生缺页中断,操作系统就检查所有页表项中计数器的值,找到值最小的一个页面,这个页面就是最近最少使用的页面。
       但是只有非常少的计算机拥有这样的硬件。

7. 最不常用页面置换算法(NFU)(Not Frequently Used)

       用一个软件模拟LRU,该算法将每个页面与一个软件计数器相关联。计数器的初值为0。每次时钟中断时,由操作系统扫描内存中所有的页面,将每个页面的R位(它是0或1)加到它的计数器上。这个计数器大体上跟踪了各个页面被访问的频繁程度。发生缺页中断时,则置换计数器值最小的页面。
       NFU的缺点是它不从不忘记任何事,比如一个页面之前频繁被访问,导致这个它的计数器很大,但是后来它不被访问了,而它的计数器的值还是很大,所以它一直不会被置换出去。

8. 老化算法

       老化算法是对NFU算法的修改,其修改包括两个部分,首先,在R位被加进之前将计数器右移一位,其次,将R位加到计数器最左端的位而不是最右端的位。
       老化算法中的计数器只有有限位数,如果时钟滴答是20ms,8位一般是够用的。假如一个页面160ms没有被访问过,那么它很可能并不重要。

9. 工作集页面置换算法

       一个进程当前正在使用的页面的集合称为它的工作集。
       若每执行几条指令就产生一次缺页中断,那么就称这个程序发生了颠簸。
       在单纯的分页系统中,刚启动进程时,在内存中并没有页面。在CPU试图读取第一条指令时就会产生一次缺页中断,使操作系统装入含有第一条指令的页面,其他由访问全局数据和堆栈引起的缺页中断通常会紧接着发生。一段时间后,进程需要的大部分页面都已经在内存了,进程开始在较少缺页中断的情况下运行。这个策略被称为请求调页
       有不少分页系统会设法跟踪进程的工作集,以确保在让进程运行以前,它的工作集就已经在内存中了。该方法称为工作集模型,大大减少缺页中断率。在进程前装入其工作集页面也称为预先调页。工作集是随时间变化的。
       事实上大多数程序会任意访问一小部分页面,工作集随时间缓慢变化。当程序重新开始时,就有可能根据它上次结束时的工作集对要用到的页面做一个合理的推测,预先调页就是在程序IXUS运行之前预先装入推测的工作集的页面。
       按照以前的方法,定义工作集为前1000万次内存访问锁使用过的页面的集合,那么现在就可以这样定义:工作集即是过去10ms中的内存访问所用到的页面的集合。这样的模型很合适而且更容易实现。要注意到,每个进程只计算它自己的执行时间。因此,如果一个进程在T时刻开始,在(T+100ms)的时刻使用了40msCPU时间,对工作集而言,它的时间就是40ms。一个程序从它开始执行到当前所实际使用的CPU时间总数通常称作当前实际运行时间。通过这个近似的方法,进程的工作集可以被称为在过去的τ秒实际运行时间中它所访问过的页面的集合。
       基于工作集的页面置换算法就是找出一个不在工作集中的页面并淘汰它。每个表项至少包含两条信息:上次使用该页面的近似时间和R(访问位)。

       过程:扫描所有的页面检查R位:
              若(R == 1)
                     设置上次使用时间为当前实际时间,以表示缺页中断时该页面正在被使用
              若(R == 0 且生存时间>τ)
                     移出这个页面,该页面在当前时钟滴答中未被访问,不在工作集中,用新的页面置换它。扫描会继续进行以更新剩余的表项。
              若(R == 0 且生存时间≤τ)
                     记住最小时间。如果该页面R==0且生存时间小于或等于τ,则页面仍在工作集中。把页面临时保存下来,但是要记住生存时间最长(“上次使用时间”的最小值)。如果扫描完整个页表却没有找到合适的淘汰的页面,如果找到了一个或多个R == 0的页面,就淘汰生存时间最长的页面。
       在最坏的情况下,在当前时钟滴答中,所有的页面都被访问过了,也就是所有的R都为1,因此就随机选择一个页面淘汰,如果有的话最好选一个干净页面。

10. 工作集时钟页面置换算法

       在工作集页面置换算法中中,当缺页中断发生后,需要扫描整个页表才能确定被淘汰的页面,因此基本工作集算法是比较费时的。
       基于时钟算法,并且使用了工作集信息,被称为WSClock(工作集时钟)算法。由于它实现简单,性能较好,所以在实际工作中得到了广泛应用。
       与时钟算法一样,所需的数据结构是一个以页框为元素的循环表。最初,该表示空的,当装入第一个页面后,把它加到该表中。随着更多的页面加入,它们形成一个环。每个表项包含来自基本工作集算法的上次使用时间,以及R位和M位。
       与时钟算法一样,每次缺页中断时,首先检查指针指向的页面。如果R位是1,该页面在当前时钟滴答中就被使用过,那么该页面就不适合被淘汰。然后把该页面的R位置为0,指针指向下一个页面,并重复该算法。
       如果R位是0,查看生存时间,如果生存时间大于τ并且该页面是干净的,它就不在工作集中,而且在磁盘上它有一个有效的副本。申请此页框,并把新页面放在其中。如果该页面已经被修改过,就不立即申请此页框,为了避免由于调度写磁盘操作引起的进程切换,指针继续向前走,算法继续对下一个页面进行操作,有可能存在一个旧的而且干净的页面可以立即使用。
       原则上,所有的页面都有可能因为磁盘I/O在某个时钟周期被调度,为了降低磁盘阻塞,需要设置一个限制,即最大只允许写回n个页面。一旦达到该限制,就不允许调度新的写操作。
       指针经过一圈返回它的起点,有两种情况:

  • 至少调用了一次写操作
  • 没有调用过写操作
           对于第一种情况,执行了写操作的页面已经是干净的了,置换遇到的第一个干净页面,这个页面不一定是第一个被调度写操作的页面,因为硬盘驱动程序为了优化性能可能已经把写操作重排序了。
           对于第二种情况,所有的页面都在工作集中,否则将至少执行了一个写操作。由于缺乏额外的信息,一个简单的方法就是随便置换一个干净的页面来使用,扫描中需要记录干净页面的位置。如果不存在干净页面,就选定当前页面并把它协会磁盘。

你可能感兴趣的:(Linux)