FIFO页面置换必须用std::queue配合unordered_set维护驻留状态,缺页判断、淘汰更新和命中率统计均需严格遵循时间戳顺序与总访问次数分母。

FIFO 页面置换不能只靠 std::queue 就跑通——缺页判断失效、命中率虚高、淘汰错位,几乎必然发生。
为什么 std::queue 单独用会算错命中率
因为 std::queue 只管进出顺序,不提供 O(1) 查找能力。每次访问页号时,你必须立刻知道它“是否已在内存中”,否则:本该命中的访问被当成缺页,cnt++ 多加一次;新页重复装入,物理块实际未满却被误判为满,导致不该淘汰的页被踢出。
常见错误写法:
queue<int> q; unordered_set<int> in_mem; // ✅ 正确配套 // 但若漏掉下面这行: in_mem.insert(page); // ❌ 淘汰后没 erase(q.front()),后续再访同一page仍查不到
- 仅用
std::queue+ 遍历判断存在性 → 时间复杂度升为 O(m),m 是物理块数,m=64 时单次查找就多 64 次比较 - 插入新页时忘了往
unordered_set加 → 所有后续访问都判为缺页 - 淘汰页后只
q.pop(),没in_mem.erase(q.front())→ 集合残留脏数据,命中逻辑彻底崩溃
cur 指针在循环数组里怎么不出界
手写循环数组模拟 FIFO 队列时,cur 表示“下次该淘汰的位置”,更新必须满足两个硬约束:只在缺页且内存已满时才动;更新后必须严格对物理块数 m 取模。
立即学习“C++免费学习笔记(深入)”;
典型陷阱:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 初始化
cur = 0,但分配数组时用了new int[m+1]或malloc(sizeof(int)*100)→ 实际可用空间 ≠ m,(cur + 1) % m算出来下标合法,但写入越界 - 缺页未满时也执行
cur = (cur + 1) % m→ 淘汰指针空转,逻辑头部漂移,最老页丢失 - 用
cur++后再手动if (cur >= m) cur = 0→ 多线程或中断场景下可能漏判,不如直接cur = (cur + 1) % m原子
命中率分母必须是总访问次数,不是“有效访问”或“装入次数”
操作系统定义明确:缺页率 = 缺页次数 / 总页面访问次数。这个“总次数”就是输入序列长度 n,一个都不能少,包括首次装入前的所有访问。
例如序列 7 0 1 2 0 3 ... 共 17 个数,无论前 3 次是否填满内存,全部计入分母。
- 误把“内存未满阶段”跳过统计 → 分母变小,命中率人为抬高(比如 17 次访问只算后 14 次,结果失真)
- 把重复装入同一页面的次数去重 → 分母缩水,尤其在短周期重复序列(如 0 1 2 0 1 2)中误差爆炸
- 输出时写成
hit / (n - cnt)(用命中数除以非缺页数)→ 数学定义错误,不是命中率,是“命中占非缺页比”
淘汰逻辑必须绑定“进入内存时间”,不是“访问序列位置”
FIFO 的“先进”指页面调入内存的时间戳,不是它在原始访问串里的下标。当页号反复出现(如 0 1 2 0 1 3),必须确保:每次新装入都 append 到逻辑队尾;淘汰永远从逻辑队头取;中间命中不改变队列结构。
这意味着不能靠 vector<int> 存历史访问来推断谁该淘汰——历史访问和驻留状态是两件事。
- 用
vector记录所有访问,然后按索引取“最早出现的页” → 错!那只是访问最早,不是驻留最早 - 命中时把该页挪到队尾(像 LRU 那样)→ 彻底破坏 FIFO 语义,变成伪 LRU
- 用
queue但每次命中都pop再push→ 队列顺序乱,最老页位置不可预测
真正关键的是:装入即入队尾,淘汰即出队头,命中则静默——三者互斥,不可交叉。

















