不能直接用std::queue实现fifo页面置换,因其无法快速判断页是否驻留内存,需配合unordered_set等结构同步维护驻留状态,否则命中判断失效、缺页统计错误;循环数组实现时cur仅在缺页且满时更新,且须严格按物理块数分配数组。

FIFO 页面置换在 C++ 里不是靠 std::queue 就能直接“开箱即用”的——它必须和内存驻留状态、缺页判断、淘汰位置更新三者严格同步,否则命中率会算错,甚至把不该淘汰的页踢出去。
为什么不能直接用 std::queue 存页面号就完事?
因为 std::queue 只管进出顺序,不记录“当前哪些页还在内存里”。FIFO 调度时,每次访问都要先查:这个页号是否已在内存中?若已存在,就不该触发淘汰,也不该计入缺页;但仅靠 queue 无法快速完成这个查找(得遍历整个队列)。实际实现中,必须额外维护一个集合(如 std::unordered_set)或数组标记驻留状态。
- 只用
std::queue+std::vector模拟队列内容,但不建索引 → 查页是否存在要O(m)扫描,m 是物理块数,小数据看不出问题,块数一多(比如 m=64)+ 长访问序列(n=10000)就会明显拖慢 - 用
std::queue但没同步更新std::unordered_set→ 命中判断失效,导致本该命中的页被重复装入,缺页数虚高 - 淘汰后没清空旧页在集合里的记录 → 后续再访问该页会被误判为“不在内存”,造成二次缺页
FIFO 算法中 cur 指针和取模运算的边界陷阱
很多手写 FIFO 用循环数组 + cur 下标模拟队列头,靠 cur = (cur + 1) % m 移动。这本身没问题,但容易漏掉两个关键前提:
- 只有发生缺页且内存已满时,才该移动
cur;如果页已命中,cur绝对不能动 —— 否则下次淘汰位置就偏了 - 当
m == 1时,(cur + 1) % 1恒为 0,逻辑正确;但若初始化cur为 -1 或未初始化,%运算在负数下行为依赖编译器(C++11 起规定向 0 截断,但旧代码可能出错) - 数组大小必须严格等于物理块数
m,不能按最大值(如MAXSIZE=100)静态分配后只用前m个 —— 否则cur越界写入会覆盖无关变量
命中率分母到底是页面访问次数还是有效访问次数?
缺页率定义是 缺页次数 / 总访问次数,分母就是输入的页面序列长度 n,不是“去重后长度”也不是“实际装入次数”。但实操中常有人把首次装入(缺页)和后续命中混在一起统计,导致分子/分母错位。
- 示例序列
7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1共 17 个访问,无论中间多少次命中,分母固定是 17 - 若某页第一次访问缺页,第二次访问命中 → 这两次都计入分母,只第一次计入分子
- 如果程序把“内存填满前的所有访问”单独跳过计数(比如认为“不算真正调度”),命中率就会失真 —— 实际 OS 中,哪怕只剩 1 块空闲,首次装入也算一次缺页中断
最容易被忽略的是:FIFO 的“先进”指进入内存的时间,不是访问序列里的位置。当多个页反复交叉访问(如 0 1 2 0 1 3),靠简单队列头尾无法体现“谁更老”,必须确保每次装入都追加到逻辑尾部、淘汰固定从逻辑头部取 —— 这一点在用数组模拟时,cur 必须代表“下一个被淘汰者的位置”,而不是“当前最老页的位置”。稍不注意,cur 就会变成指向“刚装入的页”,彻底反转淘汰逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











