Lecture 19: Memory Management 6
本章小结:
在使用虚拟内存时,需要做出几个关键决策:
- 何时将页面加载到内存中
- 哪些页面会被从内存中移除 ⇒ 页面替换算法
- 为一个进程分配多少页面,以及这些页面是局部的还是全局的
- 何时将页面从内存中移除 ⇒ 分页守护进程
虚拟内存中可能出现哪些问题 ⇒ 超负荷(thrashing)
页面替换
概念
当新页面被加载且所有页面都被占用时,操作系统必须选择一个页面进行删除。
这一选择是由页面替换算法做出的,并会考虑以下因素:
- 该页面最后一次被使用的时间/预计下次再次使用的时间
- 该页面是否已被修改(只有被修改的页面需要进行写入操作)
- 必须明智地做出替换选择(即非随机选择),以节省时间/避免出现混乱状态。
算法
- 最优(Optimal)页面替换
- 先进先出(FIFO)页面替换:①第二次机会替换 ②时钟替换
- 不最近使用(Not recently used,NRU)
- 最近最少使用(Least recently used,LRU)
最优页面替换
在理想/最优的世界里
- 每一页都会被标注出将要执行的指令数量或再次被使用之前的时长
- 那些长时间内都不会被引用的页面才是最应该被移除的页面
这种最优策略无法实际应用
- 它可以用于执行后的分析——即最小的页面故障次数会是多少
- 它为页面故障次数提供了一个下限(用于与其他算法进行比较)
先进先出(FIFO)页面替换
FIFO 会维护一个链表,并且新页面会添加到链表的末尾。
当发生页面错误时,链表头部的最旧页面会被淘汰。
FIFO 的优点和缺点包括:
- 它易于理解/实现
- 其性能不佳 ⇒ 无论是使用频繁的页面还是使用较少的页面,被淘汰的可能性都相同
模拟:
假设我们有一个包含八个逻辑页面和四个物理帧(physical frames,PFs)的系统。
考虑以下页面引用顺序:
0 2 1 3 5 4 6 3 7 4 7 3 3 5 5 3 1 1 1 7 2 3 1 4
所产生的页面错误次数为 13 次。
……
第二次机会替换(Second Chance FIFO)
二次替换先进先出策略:
- 如果列表前端的页面未被引用,则将其淘汰。
- 如果设置了引用位,则将该页面置于列表末尾,并重置其引用位。
二次替换先进先出策略比先进先出策略效果更好,但实现起来成本较高(因为列表会不断变化),而且如果所有页面最初都被引用过,它可能会退化为先进先出策略。

时钟替换
二次机会先入先出算法可以通过维护一个循环列表来得到改进:
- 一个指针指向“已访问”的最后一页
- 该算法被称为(单手)时钟算法
- 对于长列表来说,其运行速度较慢
维护列表所花费的时间减少了

不最近使用(Not recently used,NRU)
被引用和修改的位会保存在页表中:初始时,被引用的位会被设为 0,并会定期重置(例如,系统时钟中断或在搜索列表时进行重置)。
存在四种不同的页面“类型”:
- 类 0:未被引用且未被修改
- 类 1:未被引用且被修改
- 类 2:被引用但未被修改
- 类 3:被引用且被修改
每当发生页面错误时,都会检查页面表条目。
可以这样实现:
- 从类 0 中找到一个要删除的页面
- 如果步骤 1 失败,则再次扫描以查找类 1 的页面,并将访问过的每个页面的引用位设置为 0
- 如果步骤 2 失败,则从步骤 1 重新开始(类 2 和 3 的元素现在已变为类 0 或 1)
NRU 算法具有良好的性能,并且易于理解和实现。
最近最少使用(Least recently used,LRU)
最近最少使用算法会淘汰未被使用时间最长的页面。
- 操作系统会记录页面的最后使用时间。
- 每个页面表条目都包含一个计数器字段。
- 这种实现方式成本较高,因为它需要一个按使用顺序排列的页面列表。
该算法可以通过硬件实现,使用一个在每条指令后递增的计数器。
假设我们有一个包含八个逻辑页面和四个物理帧(physical frames,PFs)的系统。
考虑以下页面引用顺序:
0 2 1 3 5 4 6 3 7 4 7 3 3 5 5 3 1 1 1 7 2 3 1 4
所产生的页面错误次数为 12 次。
……
总结
最佳页面替换:最佳但不实用
FIFO 页面替换:性能较差但易于实施
- 第二次机会更换:性能提高但执行不佳
- 时钟替换:改进了实现,但仍然可能很慢
最近未使用(NRU):易于理解,效率适中(接近LRU)
最近最少使用 (LRU):接近最佳,但更难实现
常驻集
常驻集的大小
应为单个进程分配多少页:
- 小型常驻集可以在内存中存储更多进程 ⇒ 提高 CPU 利用率
- 小型常驻集可能会导致更多的页面错误
- 大型常驻集可能不再降低页面错误率(收益递减)
常驻集的大小和系统利用率之间存在权衡
常驻集大小可以是固定的或可变的(即在运行时调整)
对于可变大小的常驻集,替换策略可以是:
- 局部:替换同一进程的页面
- 全局:可以从不同的进程中取出页面
使用局部作用域时,需要仔细评估其大小(通常基于工作集或页面错误频率)

局部 (c) 全局
局部页面替换vs.全局页面替换
全局替换策略可以从整个集合中选择帧,即可以从其他进程“获取”它们
- 帧动态分配给进程
- 进程无法控制自己的页面错误频率,即一个进程的PFF受到其他进程的影响
局部替换策略只能选择分配给当前进程的帧
- 每个进程都有固定的内存比例
- 局部“最旧的页面”不一定是全局的“最旧页面”
Windows 使用带有局部替换的变量方法
前面介绍的页面替换算法可以使用这两种策略。
工作集
常驻集包括内存中的进程页集
工作集 W(t,k) 包括流程的最后 k(= 工作集窗口)虚拟时间单位中的集合引用页
k 可以定义为“内存引用”或“实际处理时间”
- 最近使用的页面集
- 在预先指定的时间间隔内使用的页面集
工作集大小可用作应分配给流程的数框的指南
例子:按顺序考虑以下页面引用
如果 k = 3:
- 在 t1 时,W(t1,3) = {4,5,6}
- 在 t2 时,W(t1,3) = {4,7}
如果 k = 5:
- 在 t1 时,W(t1,5) = {2,3,4,5,6}
- 在 t2 时,W(t1,5) = {2,4,7}
工作集是时间 t 的函数:
- 进程在位置之间移动,因此,工作集中包含的页面随时间变化
- 稳定的间隔与快速变化的间隔交替出现
|W(t,k)|则在时间上可变。具体说来:1 ≤ |W(t,k)|≤ min(k,N)
其中 N 是进程的总页数。
为 k 选择正确的值至关重要:
- 太小:不准确,页面丢失
- 太大:存在太多未使用的页面
- 无穷大:进程的所有页面都在工作集中
工作集可用于指导常驻集的大小
- 监视工作集
- 从驻留集中删除不在工作集中的页面
工作集的维护成本很高⇒页面错误频率 (page fault frequency,PFF) 可以用作近似值
- 如果 PFF 增加 ->我们需要增加 k
- 如果 PFF 非常降低 ->我们可以尝试减少 k
分页守护程序(paging daemon):预清理(⇔需求清理)
主动保留一些空闲页面以备将来的页面错误使用会更有效
- 如果没有,我们可能必须找到一个要驱逐的页面,并在发生页面错误时首先将其写入驱动器(如果已修改)
许多系统都有一个称为分页守护程序的后台进程
- 此过程定期运行
- 它检查帧的状态,如果空闲帧太少,它会选择要逐出的页面(使用页面替换算法)
分页守护进程可以与缓冲(空闲和修改列表)结合使用,⇒写入修改后的页面,但尽可能将它们保留在主内存中
颠簸(Thrashing)
定义抖动
假设所有可用页面都在使用中,并且需要加载新页面:即将被逐出的页面必须在不久之后重新加载,即它仍然处于活动状态
当碎片被更换并立即再次加载时,就会发生抖动(颠簸)
恶性循环?
CPU 利用率太低 ⇒调度程序会提高多编程程度 ⇒ 帧被分配给新流程并从现有流程中删除 ⇒ I/O 请求因页面错误而排队
CPU 利用率进一步下降 ⇒ 调度器提高了多编程程度
原因/解决方案
抖动的原因包括:
- 多编程程度过高,即总需求(即所有工作集大小的总和)超过供应(即可用帧)
- 单个进程分配的页数太少
这可以通过例如使用良好的页面替换策略、降低多编程程度(中期调度程序)或添加更多内存来防止
页面错误频率可用于检测系统是否正在抖动
理解
FIFO vs. 最佳页面替换算法
将 FIFO/LRU 与最佳页面替换算法进行比较。该进程启动时内存中没有任何页面。
最佳方法产生的最小页面错误数是多少?
步骤 1:统一前提
进程最初 内存中没有任何页(冷启动)。
页面访问串:
0 2 1 3 5 4 6 3 7 4 7 3 3 5 5 3 1 1 1 7 2 3 1 4
(共 24 次访问)。
步骤 2:计算最优(OPT,Belady 最小页故障)
OPT 每次都把 下次使用最远(或永不使用) 的页面换出;
帧数未知,但图里给出了 4 个 PF
槽(图中 PF1 … PF4
),说明 物理帧 = 4。
按 4 帧模拟 OPT:
访问 | 帧0 | 帧1 | 帧2 | 帧3 | 页故障? | 淘汰页 |
---|---|---|---|---|---|---|
0 | 0 | – | – | – | ✔ | – |
2 | 0 | 2 | – | – | ✔ | – |
1 | 0 | 2 | 1 | – | ✔ | – |
3 | 0 | 2 | 1 | 3 | ✔ | – |
5 | 0 | 2 | 1 | 5 | ✔ | 3(3 下一次在第 7,最远) |
4 | 0 | 2 | 1 | 4 | ✔ | 1(1 下一次在第 17) |
6 | 0 | 2 | 6 | 4 | ✔ | 2(2 下一次在第 21) |
3 | 0 | 2 | 6 | 3 | ✖ | – |
7 | 0 | 7 | 6 | 3 | ✔ | 4(4 不再使用) |
4 | 0 | 7 | 6 | 4 | ✔ | 0(0 不再使用) |
7 | – | 7 | 6 | 4 | ✖ | – |
3 | – | 7 | 6 | 3 | ✖ | – |
3 | – | 7 | 6 | 3 | ✖ | – |
5 | 5 | 7 | 6 | 3 | ✔ | 6(6 不再使用) |
5 | 5 | 7 | – | 3 | ✖ | – |
3 | 5 | 7 | – | 3 | ✖ | – |
1 | 5 | 7 | 1 | 3 | ✔ | 7(7 下一次在第 20) |
1 | – | – | 1 | 3 | ✖ | – |
1 | – | – | 1 | 3 | ✖ | – |
7 | – | 7 | 1 | 3 | ✖ | – |
2 | 2 | 7 | 1 | 3 | ✔ | 5(5 不再使用) |
3 | 2 | 7 | 1 | 3 | ✖ | – |
1 | 2 | 7 | 1 | 3 | ✖ | – |
4 | 2 | 7 | 1 | 4 | ✔ | 3(3 不再使用) |
统计页故障:共 14 次。
结论
在 4 个物理帧 的前提下,最优(OPT)算法的最小页故障数为 14。