单独用 std::queue 实现 FIFO 页面置换必然算错命中率,因其仅管理淘汰顺序而无法 O(1) 判断页是否驻留或同步清理状态;必须配合 std::unordered_set 维护存在性,二者操作严格原子对齐:装入时 push+insert、淘汰时 pop+erase、命中时仅查 set;分母恒为总访问次数,不可跳过初始装入阶段。

单独用 std::queue 实现 FIFO 页面置换,命中率一定算错——它不维护驻留状态,查“页是否已在内存”只能遍历,淘汰后残留旧页号在逻辑集合里,导致后续访问持续误判缺页。
为什么 std::queue 单独用不了 FIFO 页面置换
FIFO 调度有三个不可拆分的动作:① 快速判断页是否驻留;② 缺页时决定是否淘汰;③ 淘汰后同步清除该页的驻留标记。而 std::queue 只做②,不做①和③。
-
std::queue没有find()成员函数,写q.find(page)直接编译失败 - 退而求其次遍历判断存在性 → 时间复杂度升为
O(m),m=64时单次查找就多 64 次比较 - 只调用
q.pop()但没同步inMemory.erase(victim)→ 旧页号仍留在集合中,后续再访仍报缺页 - 插入新页时只
q.push(page)却漏掉inMemory.insert(page)→ 所有访问都判为缺页
必须同步维护 std::queue 和 std::unordered_set
这是最简可行组合:队列管“谁最早进来”,哈希集管“有没有它”,二者操作必须严格对齐。
- 命中判断只用
inMemory.find(page) != inMemory.end(),不碰queue,不增pageFaults - 缺页时先
pageFaults++,再检查inMemory.size() == frameCount(满才置换) - 置换前必须确认
!pageQueue.empty(),否则front()行为未定义(尤其frameCount == 0或序列极短时) - 淘汰动作必须成对:
int victim = pageQueue.front(); pageQueue.pop(); inMemory.erase(victim); - 装入动作也必须成对:
pageQueue.push(page); inMemory.insert(page);
手写循环数组时 cur 指针的边界陷阱
用 std::vector<int> frames(frameCount, -1)</int> + int cur = 0 模拟队列,比 STL 更贴近底层,但也更易越界或逻辑漂移。
立即学习“C++免费学习笔记(深入)”;
-
cur是“下次该覆盖的位置索引”,不是“当前最老页位置”——最老页永远在frames[cur] -
cur只在发生缺页且内存已满时才更新:cur = (cur + 1) % frameCount - 数组大小必须严格等于
frameCount,多一分可能覆盖相邻变量,少一分导致cur越界写入 - 首次装入阶段(内存未满)绝不能动
cur,否则最早装入的页会被跳过淘汰 - 即使
frameCount == 1,也要用取模:(cur + 1) % 1恒为 0 才安全
命中率分母为什么必须是总访问次数
缺页率 = pageFaults / pageReferences.size(),这个分母就是输入页面序列长度,一个都不能少。
- 示例序列
7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1共 17 次访问 → 分母就是 17 - 若程序把前 3 次(填满驻留集)单独过滤掉计数 → 命中率分子/分母全部偏移,结果不可比
- 真实 OS 中,
pagefault计数器从第一次访存就开始累加,FIFO 模拟必须对齐这一语义 - 误把“内存未满阶段”跳过统计 → 分母变小,命中率人为抬高
最容易被忽略的是:cur 不是自增计数器,而是淘汰锚点;inMemory 不是可选辅助,而是判断命中的唯一依据;分母不是“有效调度次数”,而是原始序列长度——这三个点一旦松动,整个模拟就失去参考价值。


















