当访问到一个地址是未映射的地址时,MMU注意该页面没有被映射,会让CPU陷入操作系统中,这个陷阱称为缺页中断或缺页错误,操作系统会找到一个很少使用的物理内存的页框然后将其内容写入磁盘,然后将需要访问的页面读到刚刚回收的页框中,修改映射关系即可。
当发生缺页中断时,虽然可以随机选择一个页面来置换,但如果每次选择不常使用的页面会提高系统的性能。如果一个频繁使用的页面被置换出内存,很可能短时间内又要调入内存,这会带来不必要的开销。
在接下来讨论的所有页面置换算法中都存在一个问题,当需要从内存中换出某个页面时,它是否只能是缺页进程自己的页面?这个要换出的页面是否可以属于另外一个进程?
最优页面置换算法
在缺页中断发生时,有些页面在内存中,其中一个页面将很快被访问,其他页面可能要到1000条指令后才会被访问,最优页面置换算法规定应该置换标记最大的页面,也就是最多指令内不会被访问的页面,比如一个是800万条指令内不会被使用,另一个页面是600万条指令内不会被使用。则置换前一个指令。
这个算法的唯一的问题是它是无法实现的,当缺页中断发生时,操作系统无法知道各个页面下一次将在什么时候访问。
最近未使用页面置换算法NRU
在大部分具有虚拟内存的计算机中,系统为每个页面设置了两个状态位,当页面被访问时(读或写)设置R位,当页面被写入(修改)设置M位,可以用R位和M位来构造一个算法:R位定期清零,以区分最近没有被访问的页面和被访问的页面。NRU淘汰一个没有被访问的页面即可。
先进先出页面置换算法FIFO
由系统维护一个所有当前在内存中的页面的链表,最新进入的页面放在表尾,最早进入的页面放在表头,当发生缺页中断时,淘汰表头的页面并把新调入的页面放在表尾。
但是这个算法的问题在于也可能会淘汰最常使用的页面。
第二次机会页面置换算法
检查最老页面的R位,如果R位是0,那么这个页面又老又没有使用,可以立即置换,如果是1,就将R位清0,并把该页面放在链表尾端,继续从表头搜索。
时钟页面置换算法
尽管第二次机会算法较为合理,但经常要在链表中移动页面,效率低。一个更好的办法是把所有的页面都保存在一个类似钟面的环形链表中,一个表针指向最老的页面。
当发生缺页中断时,算法检查表针指向的页面,如果R位是0就淘汰该页面,并把新的页面插入这个位置,然后表针前移一位,如果R位是1就清除R位并把表针前移一位,重复这个过程直到找到一个R位为0的页面为止。
最近最少使用页面置换算法LRU
在缺页中断发生时,置换未使用时间最长的页面,为了完全实现LRU,需要维护一个所有页面的链表,最近最多使用的在表头,最近最少使用的在表尾,困难的是每次访问内存时都必须更新整个链表,在链表中找到一个页面,删除它,然后把它移动到表头是一个非常耗时的操作。
工作集页面置换算法
一个进程当前正在使用的页面的集合称为它的工作集,如果整个工作集都被装入到内存,那么进程在运行到下一运行阶段之前,不会产生很多缺页中断。
工作集页面置换算法,基本思路就是找出一个不在工作集中的页面并淘汰它。
工作集时钟页面置换算法
当缺页中断发生后,需要扫描整个页表才能确认被淘汰的页面,因此基本工作集算法是比较费时的,有一种改进的算法,基于时钟算法,并且使用了工作集信息。称为WSClock算法。
与时钟算法一样,每次缺页中断时,首先检查指针指向的页面,如果R位被置为1,该页面最近被用过,那么该页面不适合被淘汰,因此将R位置为0,指针指向下一个页面,当指向一个R位为0的页面时,如果页面的生存时间大于一定时长并且该页面是干净的。它就不在工作集中,并且在磁盘上有一个有效的副本。可申请此页框,并把新页面放在其中。
页面置换算法小结:
算法 | 注释 |
最优算法 | 不可实现,但可作为基准 |
NRU(最近未使用)算法 | LRU的粗糙的近似 |
FIFO算法 | 可能抛弃重要(常使用)页面 |
第二次机会算法 | 比FIFO有大的改善 |
时钟算法 | 现实的 |
LRU(最近最少使用)算法 | 很优秀,但难以实现 |
NFU(最不经常使用)算法 | LRU的近似 |
老化算法 | 非常近似LRU |
工作集算法 | 实现起来开销很大 |
工作集时钟算法 | 好的有效算法 |