单独用 std::queue 实现 fifo 页面置换必然算错命中率,因其无 o(1) 查找能力,无法判断页是否驻留,导致缺页误判、淘汰后残留旧页号、“假驻留”引发持续虚高缺页;正确做法是 std::queue 管入序、std::unordered_set 管驻留,二者操作严格同步。

单独用 std::queue 实现 FIFO 页面置换一定会算错命中率——它不维护驻留状态,查“页是否已在内存中”退化为 O(m) 遍历,且淘汰后残留旧页号在逻辑集合里,导致后续访问持续误判缺页。
为什么 std::queue 单独用不了 FIFO 页面置换
核心动作缺三步:查驻留、决定淘汰、同步清理。而 std::queue 只做第二步。漏掉任意一步都会崩:
-
std::queue没有find()成员函数,写q.find(page)直接编译失败 - 改用遍历判断存在性 → 单次查找耗时从 O(1) 变成 O(m),m=64 时慢 64 倍
- 只
q.push(page)不往std::unordered_set插入 → 后续所有访问都判缺页 - 只
q.pop()不in_memory.erase(victim)→ 被淘汰的页还在集合里,“假驻留”持续引发虚高缺页 - 多个连续相同页号(如
0 0 0):第一次缺页装入,后两次本该全命中,但仅靠 queue 无法识别
std::queue + std::unordered_set 同步更新必须严格对齐
二者不是“可选搭配”,是硬性闭环:一个管顺序,一个管存在性。操作必须原子同步:
- 装入新页时:
q.push(page)✅ 且in_memory.insert(page)✅ - 淘汰旧页时:
int victim = q.front(); q.pop();✅ 且in_memory.erase(victim)✅ - 查命中时:只用
in_memory.find(page) != in_memory.end()—— 不动q,也不遍历 - 初始化时:两个结构都为空,不要预填、不要假设“第一页一定缺页”
- 调用
q.front()前必须检查!q.empty(),尤其frameCount == 0或访问序列极短时
手写循环数组时 cur 下标更新的三个硬约束
用 std::vector<int> frames(frameCount)</int> + int cur 模拟队首比 STL 更贴近底层,但 cur 不是随便加一取模就能跑通:
-
cur初始值必须是0,不能是未定义值或-1(-1 % m在 C++ 中仍是 -1,越界) -
cur仅在发生缺页 且 内存已满(in_memory.size() == frameCount)时才更新:cur = (cur + 1) % frameCount - 数组大小必须严格等于
frameCount,多一分可能覆盖相邻变量,少一分会导致cur越界写入 - 首次装入阶段(内存未满)绝不能动
cur,否则最早装入的页会被跳过,最老页定义失效 - 淘汰页不是取
frames[cur]就完事——必须同时in_memory.erase(frames[cur]),否则下次访问仍误判
命中率分母为什么必须是总访问次数
缺页率 = pageFaults / pages.size(),雷打不动。这不是优化技巧,而是语义对齐:
- 输入序列
{7, 0, 1, 2, 0, 3, ...}共 17 个数 → 分母就是 17 - 第 1 次访问页 7:缺页,计入分子;第 5 次再访页 0:命中,不计分子但计入分母
- 把“前
m次填空阶段”跳过统计 → 分母变小,缺页率人为虚高 - 去重后再算分母(比如认为“同一页只算一次”)→ 在周期序列(如
0 1 2 0 1 2)中误差爆炸 - 真实 OS 的
pagefault计数器从第一次访存就开始累加,FIFO 模拟必须对齐这一行为
最容易被忽略的是:FIFO 的“先进”指页进入内存的时间戳,不是它在访问序列里的下标。交叉访问下,仅靠队列头尾无法反映谁更老——必须确保每次装入都追加到逻辑尾部、淘汰固定从逻辑头部取,且驻留集合与队列内容时刻一致。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











