Skip to content

LRU 页面置换算法

2026 大纲 三(二)4 页置换算法的 LRU 部分,含它的实现方式与栈算法性质(另三种见 OPTFIFOCLOCK)。

既然记不住未来,就多记一点过去

FIFO 的病根很清楚:命中时什么都不记,所以它分不出热点页和冷门页。

想修这个毛病,就得在命中时也留下痕迹。留什么? 上一章讲虚拟内存时用过的那条经验事实在这里再次派上用场——时间局部性: 刚被访问过的位置,很快还会被访问。

把它反过来读就是一条可用的淘汰规则:很久没被访问,就意味着接下来大概也不会被访问。 于是 LRU 记的信息是"每一页最后一次被访问是什么时候",淘汰时刻最早的那一页。

整条推理链是:程序有时间局部性 ⇒ "最近访问过"是"马上还会访问"的可观测信号 ⇒ "很久没访问"就是"接下来也不会访问"的信号 ⇒ 淘汰最后访问时刻最早者,期望损失最小。

但这条链是概率性的,不是必然的。 LRU 押的注是"过去的访问分布 ≈ 未来的访问分布"。 局部性一旦失效,它可以输得比 FIFO 还惨——这一节第一部分就要给出这样一个具体的串。

另一个代价更实在:要在每次访存(含命中)时更新信息,而访存次数比缺页多好几个数量级。 这笔开销压在最热的路径上,精确 LRU 因此几乎无法在页置换里落地。 第二部分沿"精度换开销"这条轴把三级实现排开, 终点正好接上下下一篇的 CLOCK——它是这条轴上最省的一档, 不是另起炉灶

交互可视化

加载可视化中...

一、平均更优不等于每个串都更优

这是本篇唯一需要正面回答的反差:串甲上 LRU 缺页 10 次,同一串、同样 3 个页框,FIFO 只缺页 9 次。两组数都可以逐行复核,不是哪一边算错了。

把规模缩到最小,5 次访问就能看清病灶。2 个页框,引用串 1, 2, 1, 3, 2

访问FIFO 队列(队头在左)FIFO 结果LRU 栈(栈顶在左=最近)LRU 结果
1[1]缺页[1]缺页
2[1,2]缺页[2,1]缺页
1[1,2]命中(队列不动[1,2]命中(页 1 移到栈顶
3[2,3]缺页,淘汰队头 1[3,1]缺页,淘汰栈底 2
2[2,3]命中[2,3]缺页,淘汰栈底 1

FIFO 3 次、LRU 4 次、OPT 3 次。差别全部出在第 3 次那个命中:它给了 LRU 一条信息,而这条信息恰好是误导性的。FIFO 命中不更新队列,页 1 仍排在队头,第 4 步被淘汰——而页 1 此后确实不再出现,淘汰它完全正确;LRU 把页 1 提到栈顶,于是第 4 步"最久未用"变成了页 2,可页 2 恰恰是第 5 步要访问的。

串甲 3 个页框跑 LRU 的完整淘汰走查(想核对"10 次"这个数字的每一步时展开)

页面引用串 3, 2, 1, 0, 3, 2, 4, 3, 2, 1, 0, 4(串甲,FIFO 篇用的也是它),分配 3 个页框

访问页框状态缺页?淘汰
3缺页
2缺页
1缺页
0缺页3(最久未用)
3缺页2(最久未用)
2缺页1(最久未用)
4缺页0(最久未用)
3命中
2命中
1缺页4(最久未用)
0缺页3(最久未用)
4缺页2(最久未用)

缺页 10 次,缺页率 =10/1283%。手算记法:每访问一个页面后就把它标记为"最新使用",需要淘汰时挑最旧的。同一串换成 4 个页框缺页 8 次——栈算法保证了它不会反升。

二、三级实现:沿"精度换开销"排开

精确 LRU 要的信息是"每一页最后一次被访问的相对次序",而访存每秒发生上亿次,信息越精确,访存路径上的开销越大。三个台阶就是这条轴上的三个取值:

方案每次访存做什么淘汰时做什么每次访存的软件开销时间分辨率
计数器(精确)逻辑时钟加一,把时钟值写进该页表项扫描全部页表项取时间戳最小者,O(n)一次写内存单次访存
(精确)把该页号从双向链表中间摘下、接到栈顶直接取栈底,O(1)一次链表操作单次访存
附加引用位(近似)硬件把访问位 A 置 1n 位寄存器 R 当无符号整数比,取最小者0一个周期(如 100 ms)
CLOCK(近似)硬件把访问位 A 置 1指针扫,A=0 就淘汰0只区分"本轮扫描前有没有访问过"

附加引用位法的动作是:每页配一个 n 位移位寄存器 R=Rn1Rn2R1R0;页面被访问时硬件置 A=1每隔一个固定周期,OS 把每个 R 右移一位、把 A 移入最高位 Rn1,再把 A 清 0。

"数值最小 ≈ 最久未用"的道理只有一句:高位代表更近的时间片。只要某页在最近一个周期里被访问过,它的最高位就是 1,数值必然大于所有最近一个周期没被访问过的页——二进制的位权顺序天然编码了"越近的历史越重要",这就是这套硬件的全部巧思。计数器法还有一个附带麻烦:时间戳字段会溢出,需要定期整体归一化。

栈式实现的逐步演示,看清"命中也会改写排序"(想弄明白 LRU 与 FIFO 的分野在哪一步时展开)

5 个页框,访问序列 4, 7, 0, 7, 1, 0, 1, 2(栈顶在左):

访问栈内容(顶 → 底)说明
44压栈
77, 4压栈
00, 7, 4压栈
77, 0, 47 已在栈中,从中间抽出移到栈顶
11, 7, 0, 4压栈
00, 1, 7, 40 从中间抽出移到栈顶
11, 0, 7, 41 从中间抽出移到栈顶
22, 1, 0, 7, 4五块装满;栈底 4 即最近最久未使用,下次淘汰它

注意第 4、6、7 行——这三次都是命中,栈却都动了。这正是 §一 那个最小反例的机理,也正是"LRU 栈"这个数据结构与它的栈算法性质是同一件事的两面:实现要维护的那个序列,就是理论上那个与页框数无关的排序。

Cache 块替换的 LRU 与页面置换的 LRU 逐项对照(想搞清"同名不同物"差在哪时展开)
维度Cache 块替换的 LRU页面置换的 LRU
候选集合一个组内的几路(通常 2~8 路)内存中全部页框(成千上万)
谁来执行纯硬件,每次访问都要在纳秒内完成软件(OS),只在缺页时执行
精确性路数少,可以做精确 LRU(如 4 路只需 6 位比较位)页框太多,精确实现开销不可接受,实际用近似 LRU
未命中代价几十个时钟周期一次磁盘 I/O,毫秒级,差 5 个数量级

判据是候选集合的规模:几路之间排全序,硬件用几个比较位就够;几千个页框之间排全序,就必须每次访存都写内存——这才是页面置换退而求其次用 CLOCK 的根本原因。未命中代价差 5 个数量级则解释了另一件事:页面置换舍得花软件时间选得更准,Cache 却一个周期都不能多花。

考点速记

  1. 规则:淘汰最近最久没有被使用的页面,即最后一次访问时刻最早者。依据是"时间局部性 ⇒ 最近访问过是马上还会访问的可观测信号"这条概率性推理链,押的注是"过去的访问分布 ≈ 未来的访问分布"。
  2. 它是栈算法,不会出现 Belady 异常:按"最后访问时刻"排出的序列完全由引用串决定、与页框数无关,驻留集永远恰好是序列的前 m 名 ⇒ 前 m 名必是前 m+1 名的子集。
  3. 平均更优 ≠ 逐串更优:"LRU 比 FIFO 好"只在真实程序的访问模式上按平均缺页率成立,不是对每个引用串都成立的定理。串甲配 3 页框:FIFO 9 次、LRU 10 次、OPT 7 次。
  4. LRU 输在哪个模式某页刚被访问却从此不再出现,同时另一页久未访问却马上要用——赌注反向兑现。规模化的典型情形是循环扫描 m+1 个页面而只有 m 个页框,命中率可降到 0。
  5. 三级实现沿"精度换开销"排开:① 计数器法(每次访存写时间戳,淘汰时 O(n) 扫描)② 栈法(双向链表,访存时摘链接链,淘汰 O(1))③ 附加引用位法/老化算法n 位移位寄存器,每周期右移并把访问位 A 移入最高位,淘汰时选数值最小者)。
  6. 精确 LRU 的代价压在访存路径上:前两者每次访存(含命中)都要写内存或改链表,而访存次数比缺页多好几个数量级。把附加引用位法的 n 位砍成 1 位,就是 CLOCK。
  7. LFU 与 LRU 共用同一套寄存器:不比 R数值而统计 R1 的个数,选最少者淘汰。LFU 看频率,LRU 看最后一次的时间。
  8. 访问位 A 不是时间戳:A 只有 1 位,只能回答"从上次清零到现在有没有被访问过",给不出任何次序
  9. Cache 的 LRU 与页置换的 LRU 淘汰规则同一条,实现层次完全不同。判据是候选集合的规模:Cache 组内只有 2~8 路,纯硬件几个比较位就能做精确 LRU;页框以千计,只能由 OS 在缺页时用近似 LRU

这一节在真题里被考过的形式

LRU 是四种算法里手算题最多的一个——三道选择题全是给引用串手算, 问法有三种变体,但走的是同一套流程。

  • 给已访问序列和页框数,问下一次访问某页时该淘汰谁(2015-27)。4 个页框,已访问 2,0,2,9,3,4,2,8,2,4,8,4,5,下一页是 7。⚠️ 做法不是从头模拟,而是从序列末尾往前倒着看:当前驻留的 4 页里,谁的最后一次出现位置最靠前,谁就被淘汰。倒着数,5(最后)、482 依次出现,其中 2 的最后一次最靠前 ⇒ 淘汰 2。这个倒读法比正向模拟快得多也不易错。
  • 给引用串和页框数,问产生页置换的总次数(2019-29)。4 页框、串 0,1,2,7,0,5,3,5,0,2,7,6,答 5。⚠️ 问的是置换次数不是缺页次数——前 4 次缺页只调入不淘汰,置换次数 = 缺页次数 − 页框数(前提是页框最终被装满)。
  • 给引用串和已在内存的页,问缺页次数(2025-26)。3 页框、串 {0,1,2,0,5,1,4,3,0,2,3,2,0},且 0,1,2 已调入内存,答 6。⚠️ 这道题的坑在"已调入内存"四个字——开头三次访问 0、1、2 不算缺页,一上来就算成缺页会多算 3 次。这一类题必须先看清初始状态。

复习优先级必须拿满,且必须练到手算不出错。 三道题分别对应三种问法, 把"倒着数最后一次出现位置"这个技巧练熟,绝大多数 LRU 题可以秒答。 另外两处一定要看清题面:问的是缺页次数还是置换次数初始时页框是空的还是已装了几页。 速记第二条(不会 Belady)和第三条(平均优不等于逐串优)是选择题的性质考点。

易错:把"缺页次数"和"置换次数"当成一回事。置换次数 = 缺页次数 − 页框数(页框被装满的前提下)。

易错:题目说"某几页已调入内存"时仍把开头几次访问算成缺页。先看清初始状态

易错:认为 LRU 在任何引用串上都优于 FIFO。那只是平均意义;串甲上 FIFO 9 次、LRU 10 次。

易错:认为 LRU 可能出现 Belady 异常。它是栈算法,不会。

易错:把访问位 A 当成时间戳用。A 只有 1 位,给不出任何次序。

易错:认为 Cache 里的 LRU 和页置换的 LRU 实现方式相同。前者候选只有几路可做精确 LRU,后者只能用近似

易错:把 LFU 和 LRU 搞混。LFU 看访问频率,LRU 看最后一次访问的时间

教材出处
  • LRU 用"最近的过去"作为"最近的将来"的近似、访问字段记录未被访问的时间 t:汤小丹《计算机操作系统》5.3.2 节「LRU 置换算法的描述」,p164
  • LRU 的两类硬件支持——移位寄存器 R=Rn1R0(每 100 ms 右移一位、数值最小者为最近最久未使用)与栈(访问即移到栈顶、栈底为最近最久未使用):同书 5.3.2 节「LRU 置换算法的硬件支持」,p165
  • LFU 与 LRU 共用同一套移位寄存器硬件、以及"一个时间间隔内访问 1 次与 1000 次完全等效"的缺陷:同书 5.3.2 节「最少使用(LFU)置换算法」,p166

相关知识

FIFO 页面置换算法OPT 最佳置换算法CLOCK 与改进 CLOCK 算法页框分配与回收页面置换模拟器

真题练习