操作系统学习



内存管理-页面置换算法

  • 操作系统学习
  • 前言
  • 一、最优页面置换算法
  • 二、最近未使用页面置换算法
  • 三、先进先出页面置换算法
  • 四、第二次机会页面置换算法
  • 五、时钟页面置换算法
  • 六、最近最少使用页面置换算法
  • 七、最不常用算法
  • 八、老化算法
  • 九、工作集页面置换算法
  • 十、工作集时钟页面置换算法
  • 问题
  • 总结



前言


当发生缺页中断时,操作系统系统必须在内存中选择一个页面将其换出内存。页面置换算法是用来选出被置换的页面的算法。


一、最优页面置换算法

最优页面置换算法是最好的页面算法,虽然此算法不可能实现。

算法工作方式

  1. 最优页面算法总是置换最晚被访问的页面。
  2. 每个页面都可以使用在该页面首次被访问前所要执行的指令数作为标记。缺页中断发生时,标记最大的页面将被置换。

算法无法实现原因

  1. 缺页中断发生时,无法知道各个页面下一次将在什么时候访问。
  2. 可以在仿真程序上运行程序,跟踪所有页面访问情况收集信息。但是收集的信息是针对特定的程序和输入的。

二、最近未使用页面置换算法

NRU(Not Recently Used)算法

算法工作方式

  1. 为使操作系统能够收集有用的统计信息,系统为每一页面设置了两个状态位。当页面被访问时,设置R位;当页面被修改时,设置M位。每次访问内存时更新这些位,因此由硬件来设置它们。
  2. 当发生缺页中断时,操作系统会检查所有页面的R位和M位的值,把它们分成4类:
  • 第0类:没有被访问,没有被修改
  • 第1类:没有被访问,已被修改
  • 第2类:已被访问,没有被修改
  • 第3类:已被访问,已被修改
    第3类的R位被清空后就会变成第1类
  1. 算法会随机地从类编号最小的非空类中挑选一个页面被淘汰。

如果硬件没有R位和M位,可以使用操作系统的缺页中断和时钟中断机制进行模拟:启动一个进程时,将其所有的页面都标记为不在内存;一旦访问一个页面就会发生一次缺页中断,此时操作系统就可以根据访问或修改设置页面的R位和M位。

三、先进先出页面置换算法

FIFO(First-In First-Out,先进先出算法)

算法工作方式

  1. 发生缺页中断时,按照页面的方位顺序置换最早访问的页面

四、第二次机会页面置换算法

第二次机会(second chance)算法,是FIFO算法的改进。算法会将所有访问的页面按照访问的先后顺序存储在链表中。实际上,第二次机会算法是寻找一个在最近的时钟间隔内没有被访问的页面。

算法工作方式

  1. 算法置换最老页面时,会检查页面的R位,如果 R 位是0,这个页面可以置换掉;如果 R 位是1,会将 R 位清零,并将页面设置为最新的页面。

五、时钟页面置换算法

时钟(clock)页面置换算法,是第二次机会置换算法的改进。第二次机会算法需要在链表中移动页面,既这样会降低效率又不是必要的,所以就有了时钟页面置换算法。

算法工作方式

  1. 将所有页面存储在一个环形链表中,一个指针指向最老的页面。发生缺页中断时,如果指针指向页面的 R 位为0,则直接置换页面;如果指针指向页面的 R 位为1,则清除 R 位的值并把指针前移一个位置。

六、最近最少使用页面置换算法

对最优算法的一个很好的近似是基于这样的观察:在前面几条指令中频繁使用的页面可能在后面的几条指令中使用。这个近似提示了一个可实现的算法:在缺页中断发生时,置换未使用时间最长的页面。这个策略称为 LRU (Least Recently Used,最近最少使用)页面置换算法。

算法工作方式

  1. 在内存中维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。在缺页中断发生时,置换表的页面。
  2. 在每次访问内存时,必须要更新整个链表。

LRU 实现的代价是很高的。在链表中找到一个页面,删除它,然后把它移动到表头是一个非常费时的操作。每次访问内存都必须更新整个链表也是很困难的。
然而,还是有一些使用特殊硬件实现 LRU 的方法。首先考虑一个最简单的方法,这个方法要求有一个64位的计数器C,它在每条指令执行完成后自动加1,每个页表项必须有一个足够容纳计数器值的域。在每次访问内存后,将当前C值保存到被访问页面的页表项中。缺页中断发生时,操作系统检查所有页表项中计数器的值,找到值最小的一个页面,这个页面就是最近最少使用的页面。

七、最不常用算法

NFU(Not Frequently Used,最不常用)页面置换算法,是一种用软件模拟 LRU 的算法。

算法工作方式

  1. 将每一个页面与一个计数器相连,计数器的初始值为0,每次时钟中断时,操作系统扫描内存中的所有页面,将每个页面的 R 位的值加到它的计数器上。发生缺页中断时,置换计数器值最小的页面。

NFU 的问题是不会重置计数器的值。比如,进程刚开始运行时,有一个被频繁使用的页面,其计数器值很高。但是在这之后,这个页面可能就没用了,但是因为计数器的值很大,却不会被置换出去。

八、老化算法

可以对 NFU 算法做一个小修改,以便更好的模拟 LRU 算法,修改之后的就是老化(aging)算法。其修改分为两个部分:首先,在 R 位被加进之前,先将计数器右移一位;其次,将 R 位加到计数器最左端的位。每次时钟中断之后,最后访问的页面的计数器值总是最大的。
LRU 和老化算法有两个区别,其一是在两个时钟中断之间,如果访问了两个页面,老化算法不能分清访问的先后顺序;其二是老化算法的计数器只有有限位数,如果超过了这个位数,老化算法也无法得知上一次访问页面的时间。

九、工作集页面置换算法

发生缺页中断时,淘汰一个不在工作集中的页面。假设有一个长度为 k 的移位寄存器,每进行一次内存访问就将寄存器左移一位,然后在最右端插入刚才访问的页面号。当发生缺页中断时,只要读出寄存器中的内容并排序;然后删除重复的页面。结果就是工作集。然而,维护移位寄存器并在缺页中断发生时处理它所需的开销很大,因此该技术从来没有被使用过。
一种常见的近似方法是,不是向后找最近 k 次的内存访问,而是考虑执行时间。假设过去的 s 秒访问的页面的集合是工作集,假定在每个时钟滴答中,有一个定期的时钟中断会用软件方法来清除 R 位。在处理每个表项时,都需要检查 R 位。每当缺页中断发生时,扫描页表以找出一个合适的页面淘汰之。
如果 R 位是1,就把当前实际时间写进页表项的“上次使用时间”域,以表示缺页中断发生时该页面正在使用。
如果 R 是0,那么表示该页面在当前时钟滴答中没有被访问过,它可以作为候选者被置换。为了知道它是否应该被置换,需要计算它的生存时间(当前实际运行时间减去上次使用的时间),然后和 s 比较,如果它的生存时间大于 s,那么这个页面就不在工作集中,可以用新的页面置换它。扫描会继续进行以更新剩余的表项。
如果所有页面都在工作集中,则淘汰 R = 0 的页面;如果所有页面都被访问过了,就随机选择一个页面淘汰,最好选择一个干净页面。

十、工作集时钟页面置换算法

在工作集算法中,需要扫描整个页表才能确定被淘汰的页面,因此基本工作集算法比较费时。有一种改进的算法,它基于时钟算法,并且使用了工作集信息,称为 WSClock(工作集时钟)算法。

算法工作方式

  1. 所需的数据结构是一个以页框为元素的循环表。当装入一个页面后,把它加入到改变中。随着更多页面的加入,它们形成一个环。每个表项包含来自基本工作集算法的上次使用时间,以及 R 位和 M 位。
  2. 与时钟算法一样,每次缺页中断时,首先检查指针指向的页面。
  3. 如果 R 位被置为1,该页面在当前时钟滴答中就被使用过,那么该页面就不适合被淘汰。然后把 R 位置位0,指针指向下一个页面,并重复该算法。
  4. 如果 R 位为0,如果页面的生存时间大于 s 并且该页面是干净的,它就不在工作集中,并且在磁盘上有一个有效的副本。申请此页框,并把新的页面放在其中。另一方面,如果此页面被修改过,就不能立即申请页框,因为这个页面在磁盘上没有有效的副本。为了避免由于调度写磁盘操作引起的进程切换,指针继续向前走,算法继续对下一个页面进行操作。毕竟,有可能存在一个旧的且干净的页面可以立即使用。
  5. 如果指针经过一圈返回它的起始点,会有两种情况:
    1). 至少调度了一次写操作
    2). 没有调度过写操作
    对于第一种情况,指针仅仅是不停的移动,寻找一个干净页面。既然已经调度了一个或者多个写操作,最终会有某个写操作完成,它的页面会被标记为干净。置换遇到的第一个干净页面。
    对于第二种情况,所有的页面都在工作集中,否则将至少调度了一个写操作。一个简单的方法就是随便置换一个干净的页面来使用,扫描中需要记录干净页面的位置。

问题

  1. 在最近未使用页面置换算法中,硬件没有访问位和修改位的情况下,一个进程启动时,它的所有页面的两个位会被设置为0吗?R位会被定期清零吗?

总结

算法

注释

最优算法

不可实现,但可用于基准

NRU(最近未使用算法)

LRU 的很粗糙的近似

FIFO(先进先出)算法

可能抛弃重要页面

第二次机会算法

比 FIFO 有较大改善

时钟算法

现实的

LRU(最近最少使用)算法

很优秀,但很难实现

NFU(最不经常使用算法)

LRU 的相对粗略的近似

老化算法

非常近似 LRU 的有效算法

工作集算法

实现起来开销很大

工作集时钟算法

好的有效算法

  1. 最优算法在当前页面中置换到最后访问到的页面。但是无法判断哪个页面是最后访问的页面。
  2. NRU 算法通过 R 位和 M 位的状态把页面分为四类。从编号最小的类中随机选择一个页面置换。该算法易于实现,但是性能不是很好。
  3. FIFO 算法通过维护一个页面的链表来记录它们装入内存的顺序。淘汰最老的页面,但是该页面可能正在使用。
  4. 第二次机会算法是对 FIFO 算法的改进,他在移除页面前先检查该页面是否正在被使用。如果该页面正在被使用,就保留该页面。这个改进大大提高了性能。时钟算法是第二次机会算法的另一种实现。它具有相同的特征,而且只需要更少的执行时间。
  5. LRU 算法是一种非常优秀的算法,但是只能通过特定的硬件来实现。NFU 是一种近似与 LRU 的算法,它的性能不是非常好。老化算法更近似于 LRU 算法并且可以有效的实现,是一个很好的选择。
  6. 最后两种算法都使用了工作集。工作集算法有合理的性能,但它的实现开销较大。工作集时钟算法是它的一种变体,不仅具有良好的性能,并且还能高效地实现。
  7. 总之,最好的两种算法是老化算法和工作集时钟算法,它们分别基于 LRU 和工作集。它们都具有良好的页面调度性能,可以有效地实现。