Skip to content

FIFO 页面置换算法

2026 大纲 三(二)4 页置换算法的 FIFO 部分,以及由它引出的 Belady 异常与栈算法(另三种见 OPTLRUCLOCK)。

页框满了,该踢谁

请求分页那一节留下了一个没答的问题:缺页时如果没有空闲页框,就得先淘汰一个。 淘汰哪一个?

接下来的四篇——FIFO、LRU、OPT、CLOCK——回答的都是这同一个问题, 它们不是四种并列的技术,而是沿着"用多少信息来做这个决定"排开的一条线

信息从哪来是关键。理想的决定需要知道未来:踢掉那个最久都不会再用的。 可运行中的系统看不见未来,只看得见过去。于是每种算法实际上都在回答: 过去的哪一部分信息,值得花代价记下来?

FIFO 的答案是最省的一个:只记"谁先来的"。 这个信息本来就有——页面调入时天然有先后次序,把它们串成一个队列即可, 访存时什么都不用更新,不需要任何硬件支持

代价也随之而来。既然命中时什么都不记,那么一个页被访问一万次和只被访问一次, 在队列里的地位完全一样。全局变量、常用函数、循环例程所在的热点页面, 照样会因为"进来得早"被排到队头淘汰掉。

这一节除了讲清这条代价,还要处理一件由它引出的怪事: 多分配几个页框,缺页反而更多——Belady 异常。它只会发生在 FIFO 这一类算法上, 而"为什么只发生在它这一类"正是理解全部四种算法的分类依据。

交互可视化

加载可视化中...

一、命中不更新,是代价与信息量的对应

FIFO 与 LRU 的差别不在规则复杂度,而在它们赌的东西不同:FIFO 赌"待得久的该走了",这个量在页面装入那一刻就定死了,命中不提供新信息;LRU 赌"最近用过的还会再用",命中恰恰是唯一的新信息来源,不更新它 LRU 就退化成 FIFO。

于是代价也对称:LRU 要在每次访存时更新时间戳或移动栈顶,CLOCK 要靠硬件在访问时自动置访问位,FIFO 什么都不用做——访存零开销换来的正是零信息。

队列归谁维护则由置换范围决定:局部置换时队列只含本进程的页面,随页表一起属于该进程的内存管理信息,进程切换时整个队列随 PCB 挂起,不需要任何清理动作;全局置换时队列是全系统一份、包含所有可换出页框,进程切换根本不影响它。无论哪种,队列都不随进程切换而重建——重建会把"驻留时间"信息清零,等于每次切换都退化成随机置换。

串甲 3 个页框的完整淘汰走查(想核对手算每一步、或想看清热点页怎么被误淘汰时展开)

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

访问页框1页框2页框3缺页?
33缺页
232缺页
1321缺页
0021缺页(淘汰3)
3031缺页(淘汰2)
2032缺页(淘汰1)
4432缺页(淘汰0)
3432命中
2432命中
1412缺页(淘汰3)
0410缺页(淘汰2)
4410命中

缺页次数 9 次,缺页率 =9/12=75%

注意第 7 步:页面 0 在第 4 步才刚装进来,第 7 步就因为"进来得最早"被淘汰。FIFO 淘汰的是"住得最久"的,不是"最没用"的,这两件事在有热点页面的程序里几乎总是不一致。

二、Belady 异常

增加分配的页框数,缺页次数反而增加——反直觉,但确实会发生。经典反例:页面引用串 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5(它其实就是串甲换了页号,把 3、2、1、0、4 依次改名为 1、2、3、4、5,两串逐位相同)。

页框数缺页次数
39
410(反而增加了)

断裂点在位置 7(访问页 5):B7(3)={1,2,5}B7(4)={2,3,4,5}页 1 在 3 页框里还在,在 4 页框里已被淘汰

机理很清楚:3 个页框时,页 1 在位置 4 就被换出、位置 5 又被重新调入,于是它的"调入时刻"被刷新成了位置 5,排到队尾,位置 7 时轮不到它;4 个页框时,页 1 从位置 1 装入后一直没被换出,调入时刻仍停在位置 1,位置 7 时正排在队头,第一个就被淘汰。多给的那个页框反而让页 1 少了一次"刷新排队资格"的机会。 接下来位置 8 访问页 1,3 页框命中、4 页框缺页——多出来的那一次缺页就是这么来的。

经典反例 3 框与 4 框的两张逐步表(想逐行核对 9 次与 10 次这两个数字时展开)

3 个页框(缺页 9 次)

访问框0框1框2结果
11缺页
212缺页
3123缺页
4423缺页(淘汰1)
1413缺页(淘汰2)
2412缺页(淘汰3)
5512缺页(淘汰4)
1512命中
2512命中
3532缺页(淘汰1)
4534缺页(淘汰2)
5534命中

4 个页框(缺页 10 次)

访问框0框1框2框3结果
11缺页
212缺页
3123缺页
41234缺页
11234命中
21234命中
55234缺页(淘汰1)
15134缺页(淘汰2)
25124缺页(淘汰3)
35123缺页(淘汰4)
44123缺页(淘汰5)
54523缺页(淘汰1)

按行对齐逐行比 Bt(3)Bt(4),位置 1~6 包含性质都成立,位置 7 断裂。

三、给一个新引用串,怎么判断会不会出现

没有一眼看穿的闭式判据,但有一条可操作的路:

  1. 先看算法。若是 LRU 或 OPT,包含性质恒成立,直接判定不会出现,不必算。
  2. 若是 FIFO 或 CLOCK,把 mm+1 两张逐步表都画出来,逐位比较两个驻留集:包含性质从头到尾成立 → 缺页次数一定不增;某一步断裂 → 有可能出现,但必须把整串的得失算成一笔总账才能定论。
按判据走一遍完整流程:断裂了、也兑现了,总数照样是降的(想把"必要非充分"落到具体数字上时展开)

引用串 1, 3, 4, 5, 1, 2, 4, 1, 3, 1(数据自造),FIFO,判断 3 → 4 个页框是否出现 Belady 异常。

第一步:3 个页框的逐步表。 判据要求逐位比较驻留集,所以表里必须把每一步之后的驻留集单独列一列——只记"缺不缺页"是不够的。

位置访问框0框1框2结果驻留集
111缺页
2313缺页
34134缺页
45534缺页(淘汰1)
51514缺页(淘汰3)
62512缺页(淘汰4)
74412缺页(淘汰5)
81412命中
93432缺页(淘汰1)
101431缺页(淘汰2)

3 个页框:缺页 9 次

第二步:4 个页框的逐步表。

位置访问框0框1框2框3结果驻留集
111缺页
2313缺页
34134缺页
451345缺页
511345命中
622345缺页(淘汰1)
742345命中
812145缺页(淘汰3)
932135缺页(淘汰4)
1012135命中

4 个页框:缺页 7 次

第三步:逐位比较包含性质。 断裂点告诉你"哪一页被多丢了、从哪一步开始丢"。

位置Bt(3)Bt(4)Bt(3)Bt(4)
1~5逐步逐步成立
6{1,2,5}{2,3,4,5}断裂——页 1 被 4 框多丢了
7{1,2,4}{2,3,4,5}断裂——页 1 仍缺
8成立(页 1 已被重新调入,断裂愈合)
9{2,3,4}{1,2,3,5}断裂——这次轮到页 4
10{1,3,4}{1,2,3,5}断裂——页 4 仍缺

位置 6 断裂的机理与经典反例完全一样:3 个页框时页 1 在位置 4 被换出、位置 5 又被重新调入,调入时刻被刷新到队尾;4 个页框时页 1 从位置 1 起一直没被换出,位置 6 时正排在队头,第一个就被淘汰。

第四步:算总账。 断裂已经出现,但这只说明"有可能"。把两张表逐位对照,只看结果不同的那几行:

位置访问3 框4 框谁占便宜
51缺页命中4 框 赚 1
74缺页命中4 框 赚 1
81命中缺页4 框亏 1(位置 6 断裂在这里兑现)
101缺页命中4 框 赚 1
4 框缺页=93+1=7

结论:不出现 Belady 异常(9 → 7,缺页反而减少)。位置 8 那一行证明断裂确实兑现成了一次额外缺页,但被位置 5、7、10 的三次额外命中盖过了。"断裂过、也兑现过",总数照样是降的——这就是"必要条件不是充分条件"最准确的样子。只有当亏的次数多于赚的次数时,才真的出现 Belady 异常。

四、FIFO 为什么没消失

维度FIFO
实现代价最低:一个队列,O(1) 淘汰,零硬件支持
性能最差:命中不改变队列位置,热点页面照样被淘汰
Belady 异常会出现(非栈算法)
每次访存的额外开销0

看起来一无是处,但注意最后一行——FIFO 是唯一一个"访存路径上零开销"的算法。而访存每秒发生上亿次,置换只在缺页时发生。于是真实系统走的是这条路:保留 FIFO 的循环队列骨架,只在上面补最小的一点信息——加一个访问位、队头页 A=1 就清零后放回队尾"再给一次机会",是二次机会算法;把队列换成环形、指针原地扫描而不真的搬动页面,就是 CLOCK;再补一个修改位区分脏页与干净页,就是改进 CLOCK(见 CLOCK 篇)。

所以 FIFO 不是被淘汰了,而是被当成了骨架。后面三种算法都是在"命中不留痕"这一处病根上打补丁。

考点速记

  1. 规则:淘汰最先进入内存的页面。已调入页按调入次序链成队列,替换指针始终指向队头;缺页有空框 → 新页挂队尾、指针不动;缺页无空框 → 淘汰队头、新页挂队尾、指针后移;命中时队列完全不动
  2. "命中不留痕"是全部性能问题的病根:一个页被访问一万次和只被访问一次,在队列里地位完全一样 ⇒ 含全局变量、常用函数、循环例程的热点页面照样被排到队头淘汰
  3. 它唯一真正的优点:循环队列实现、淘汰 O(1),且不需要任何硬件支持(不用访问位、不用计数器、访存时什么都不更新)。开销全部集中在缺页那一刻——它因此被保留为二次机会 / CLOCK / 改进 CLOCK 的共同骨架。
  4. 队列归属:局部置换每进程一份、全局置换全系统一份两种都不随进程切换重建
  5. 栈算法判据只看一条:淘汰排序的依据依不依赖页框数 m LRU 按"最后访问时刻"、OPT 按"下次访问位置",都只由引用串决定 ⇒ ;FIFO(及 CLOCK)按"调入时刻",而调入时刻取决于哪几步缺页、缺页与否又取决于 m不是
  6. Belady 异常:增加页框数,缺页次数反而增加。只可能出现在非栈算法上,即 FIFO 与它的变体(二次机会、简单 CLOCK、改进 CLOCK)。⚠️LRU 和 OPT 都不会出现。
  7. 栈算法"不增"≠"一定减":包含性质只保证缺页次数不会上升,完全可能持平(页框已大到装得下全部页面时再加就没用了)。
  8. 包含性质断裂是必要条件不是充分条件:断裂只说明"m+1 框在这一段丢掉了 m 框还留着的页",而 m+1 框在别处照样会多命中若干次。总缺页数是这两笔的代数和,必须算总账。

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

FIFO 本身的手算至今没单独出过选择题(手算题都给了 LRU 或改进 CLOCK), 它的考法集中在Belady 异常这个性质上,外加一道大题的分问。

  • 问哪些算法可能出现 Belady 异常(2014-30)。答仅 FIFO。LRU 和 OPT 都是栈算法,加页框缺页次数不会增加。⚠️ 判据是速记第五条——看排序依据依不依赖页框数,不要凭"哪个算法更好"去猜。这是本节最稳定的考点。
  • 给页表和 CLOCK 指针位置,分别按 FIFO 和 CLOCK 求物理地址(2010-46 第 1、2 问;第 3 问的 CLOCK 部分见 CLOCK 算法)。第 1 问拆页号:页大小 1 KB ⇒ 偏移 10 位,17CAH 的高位得页号 5。第 2 问按 FIFO 淘汰装入时刻最早的页——表中 0 号页装入时刻 130 最早,故淘汰它、新页装进 7 号页框,物理地址 = 页框号左移 10 位再拼偏移。⚠️ 这一问只看装入时刻那一列,访问位那一列在 FIFO 下完全无关——题目把访问位全给成 1 就是为了引你去看它。

复习优先级性质必须记死,手算不必单练。 速记第五、六条(栈算法判据、谁会 Belady) 是必考且反复考的;手算 FIFO 极简单,跟着 LRU 一起练就够。 第八条那个"断裂只是必要条件"属于理解层面,不会直接设问,但它能防止你用错误的方法去判断异常。

易错:认为 LRU 也可能出现 Belady 异常。只有 FIFO 及其变体(含 CLOCK)会,LRU 和 OPT 是栈算法。

易错:用"哪个算法更好"去判断会不会 Belady。判据是排序依据依不依赖页框数,与优劣无关。

易错:FIFO 手算时去看访问位。FIFO 只看装入次序,访问位是 CLOCK 才用的。

易错:认为 FIFO 命中时要把该页移到队尾。命中时队列完全不动——这正是它的病根。

易错:认为进程切换时 FIFO 队列会重建。局部置换每进程一份、全局置换全系统一份,都不随切换重建

易错:把"栈算法"理解成"加页框一定减少缺页"。它只保证不增,可能持平。

教材出处
  • FIFO 的队列与替换指针、"含全局变量与常用函数的页面得不到保护":汤小丹《计算机操作系统》5.3.1 节「先进先出(FIFO)页面置换算法」,p164
  • Belady 于 1966 年提出最佳置换算法、并以此评价其他算法:同书 5.3.1 节,p163

相关知识

请求页式管理LRU 页面置换算法OPT 最佳置换算法CLOCK 与改进 CLOCK 算法页面置换模拟器

真题练习