std::queue 单独无法实现 FIFO 页面置换,因其仅支持入队出队操作,缺乏快速查找页是否驻留(需 O(m) 遍历)和同步维护驻留状态(淘汰后未清理、装入后未标记),导致命中率计算错误。

直接用 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);
循环数组实现 FIFO 时 cur 指针的边界陷阱
手写 std::vector<int> frames(frameCount, -1)</int> + int cur = 0 模拟队列,比 STL 更贴近底层,但也更易越界或逻辑漂移。
-
cur是“下次该覆盖的位置索引”,不是“当前最老页位置”——最老页永远在frames[cur] -
cur只在发生缺页 且 内存已满时才更新:cur = (cur + 1) % frameCount - 数组大小必须严格等于
frameCount,多一分可能覆盖相邻变量,少一分导致cur越界写入 - 首次装入阶段(内存未满)绝不能动
cur,否则最早装入的页会被跳过淘汰 - 即使
frameCount == 1,也要用取模:直接cur++会越界,(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 - 第 1 次访问页 7:缺页,计入分子;第 5 次再访页 0:命中,不计分子但计入分母
- 把“前
frameCount次填空阶段”跳过统计 → 分母变小,命中率人为虚高 - 把重复页去重后再算分母 → 在短周期序列(如
0 1 2 0 1 2)中误差爆炸 - OS 真实行为从第一次访存就开始累加
pagefault计数器,模拟必须对齐
真正容易被忽略的是:FIFO 的“先进”指页进入内存的时间戳,不是它在访问序列里的下标;交叉访问(如 0 1 2 0 1 3)下,仅靠队列头尾无法反映谁更老——必须确保每次装入都追加到逻辑尾部、淘汰只从逻辑头部取,且驻留集合与队列内容时刻一致。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











