深蓝黑背景封面。左侧是一张局部推演网格:表头为访问序列前六步 1、2、3、4、1、2,下面三行是物理块 1、2、3 在每一步的内容,再下面一行「缺页」用青色 × 标出前六步全部缺页;网格下方注「3 个物理块、12 次访问,逐格推演,每格按最近使用排序」。右侧大标题「3 个主存块走一遍访问序列,LRU 缺页多少次」,副标题「最近最久未使用:淘汰最久没被碰的那一页」。左上标注「笔试复盘 · 操作系统」,右上圆角胶囊标注「Day 06 · 11-23」。 图 1:前六步推演网格,前六访全部缺页。

先说明属性:这是一篇历史笔试题复盘,题目出自本地留存的早年笔试卷(选择题),与任何公司的当前招聘无关。题目很短:一个进程分到 3 个主存块,页面访问序列是 1、2、3、4、1、2、5、1、2、3、4、5,用 LRU 置换算法,缺页多少次?选项 9、10、11、12。这类题不考知识点记忆,考的是十二步推演一步不乱的耐心,外加一个最容易踩的规则细节。

先把 LRU 规则钉死,尤其是命中也要动。

LRU(最近最久未使用)的规则:缺页且块已满时,淘汰当前最久没被访问的那一页。关键细节是——命中同样算一次「使用」,要把命中页挪到「最新」位置。很多人只在缺页时维护顺序,命中不动,结果从第 8 步开始全错。推演时给三个块里的页面排一个「从最久未用到最新」的队列,每一步都要更新这个顺序。

十二步逐格推演。

  • 第 1~3 步:访问 1、2、3,块是空的,三次缺页,块内(久→新)为 1、2、3。
  • 第 4 步:访问 4,块满,淘汰最久未用的 1,缺页。块内 2、3、4。
  • 第 5 步:访问 1,1 刚被淘汰,缺页,淘汰 2。块内 3、4、1。
  • 第 6 步:访问 2,同理缺页,淘汰 3。块内 4、1、2。
  • 第 7 步:访问 5,缺页,淘汰 4。块内 1、2、5。
  • 第 8 步:访问 1,1 在块里,命中!把 1 挪到最新。块内 2、5、1。
  • 第 9 步:访问 2,命中,挪到最新。块内 5、1、2。
  • 第 10 步:访问 3,缺页,淘汰当前最久未用的 5。块内 1、2、3。
  • 第 11 步:访问 4,缺页,淘汰 1。块内 2、3、4。
  • 第 12 步:访问 5,缺页,淘汰 2。块内 3、4、5。

数一遍:缺页发生在第 1~7 步和第 10~12 步,共 10 次,只有第 8、9 两步命中。答案是 10。

写段代码复核,顺便对比 FIFO 和 OPT。

手工推演容易眼花,用 Node.js 22.15.0 把三个算法都模拟一遍(node lru.js),LRU 每步打印块内状态:

const seq = [1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5];
function lru(seq, frames) {
  const mem = []; let faults = 0; const rows = [];
  for (const p of seq) {
    const i = mem.indexOf(p);
    if (i >= 0) {                      // 命中:挪到最新位置
      mem.splice(i, 1); mem.push(p);
      rows.push([p, false, [...mem]]); continue;
    }
    faults++;
    if (mem.length === frames) mem.shift();  // 淘汰队首:最久未用
    mem.push(p); rows.push([p, true, [...mem]]);
  }
  return { faults, rows };
}

实测输出(末三行):

LRU 缺页: 10
FIFO 缺页: 9
OPT 缺页: 7

代码逐行推演与手工一致,LRU 确实是 10 次。

深蓝黑背景概念图。主体是一张完整推演表:表头十二列是访问序列 1、2、3、4、1、2、5、1、2、3、4、5;三行物理块逐格给出 LRU 状态下的内容(块内按最久未用到最新排列);缺页行以青色 × 标出第 1 至 7 步与第 10 至 12 步共 10 次缺页,第 8、9 步命中。右侧两张卡片:青色卡「规则:缺页且块满时,淘汰当前最久未被访问的页;命中同样要把该页挪到最新位置」;紫色卡「OPT 是理论下界:淘汰未来最迟才会再用的页,实际系统做不到,只作对照」。底部总结条:本序列 LRU 缺页 10 次、FIFO 缺页 9 次、OPT 缺页 7 次——LRU 不一定赢 FIFO,但都追不上知道未来的 OPT。 图 2:十二步完整推演表,10 个青色 × 就是答案。

这组数字里藏着两个反直觉的考点。

第一,这条序列上 FIFO 只要 9 次,反而比 LRU 的 10 次少——LRU 是「经验的优等生」,不是任何序列上都赢,它赌的是「最近用过的还会再用」,遇到 1、2、3、4、5 这种周期性扫视的序列就会被坑:每次要用的页恰好都是刚被淘汰的那一页。第二,OPT(最优置换)只要 7 次:它淘汰的是「未来最迟才会再用」的页,需要预知整个序列,实际系统做不到,所以它是理论下界,专门用来给其他算法当参照物。答题时把三个数字并排摆出来,立刻就从「会算」升级为「懂权衡」。

再补一个相关的冷知识作为收尾弹药:FIFO 还存在 Belady 异常——物理块增多,缺页反而可能变多;LRU 属于栈式算法,没有这个问题。笔试里如果选项出现「增加块数一定减少缺页」,针对 FIFO 就是错的。

顺嘴一提:真机里的 LRU 是「近似」的。

严格的 LRU 要求每次访问都更新全量顺序,硬件代价太高,真实操作系统用的是它的近似版:给每页配一个访问位,页被访问时硬件置位,置换时用时钟算法(Clock)转圈扫描,遇到访问位为 0 的页就淘汰,为 1 就清掉再放它一马。效果接近 LRU,开销却只是个环形指针——笔试考严格推演,工程里跑的是近似,两个口径都要知道。

复盘这类题的方法论只有一条:别怕麻烦,画表。表头写序列,三行写块内容,一行标缺页,十二格填完答案自然浮出来,比脑子里空转可靠得多。你当年是画表派还是心算派?有没有在「命中要不要更新顺序」上栽过跟头?欢迎对答案。