fifo页面置换必须配合std::queue与std::unordered_set使用:queue管理入队顺序,unordered_set支持o(1)查找;缺页率严格按访问序列总长度计算。

std::queue 单独用在 FIFO 页面置换里会崩
不能只靠 std::queue 存页号就跑通 FIFO——它不提供查找能力,每次判断“该页是否已在内存中”都得遍历整个队列,时间复杂度升到 O(m)(m 是物理块数)。更糟的是,编译器根本不会报错:你写 q.find(page) 会直接编译失败,因为 std::queue 根本没有 find 成员函数。
常见错误表现:
- 本该命中的访问被当成缺页,重复装入同一页面 →
pageFaults虚高 - 淘汰旧页后没从驻留集合里删掉它的记录 → 后续再访该页又被误判为缺页,二次计数
- 队列已空或状态不同步时调用
q.front()→ 访问未初始化内存,行为未定义
必须配对使用 std::queue + std::unordered_set
正确结构是两个组件严格同步:std::queue<int></int> 管“谁先进来”,std::unordered_set<int></int> 管“有没有它”。二者操作必须一一对应,漏掉任意一环都会导致逻辑错位。
关键操作要点:
- 装入新页时:
q.push(pageId)✅ 且residentSet.insert(pageId)✅ - 淘汰旧页时:
int victim = q.front(); q.pop();✅ 且residentSet.erase(victim)✅ - 查命中时:只用
residentSet.find(pageId) != residentSet.end()—— 不动q,也不遍历 - 初始化时:确保
residentSet为空,q为空,pageFaults = 0
循环数组实现 FIFO 时 cur 下标更新有硬约束
手写循环数组(比如 std::vector<int> frames</int>)比用 STL 更易暴露边界问题。核心陷阱不在“怎么算下标”,而在于“什么时候才动下标”。
cur 更新必须满足三个条件同时成立:
- 发生缺页(即当前页不在
residentSet中) - 内存已满(
q.size() == frame_count或等价判断) - 且仅在此刻执行
cur = (cur + 1) % frame_count
其他情况都不能动 cur:首次装入阶段(内存未满)动了,会导致最早装入的页被跳过;命中时不重置计时或清空标记,会让 cur 指向错误位置。
缺页率分母必须是访问序列总长度
缺页率 = pageFaults / pages.size(),雷打不动。不是“去重页数”,不是“填满后的访问次数”,也不是“有效调度次数”。哪怕前 m 次全是缺页(内存从空到满),这 m 次全计入分母;某页第一次缺页、第二次命中,这两访问都进分母,只第一次进分子。
最容易崩的写法是把“首次装入阶段”当作初始化跳过统计——这会让分母变小,缺页率人为虚高。而真实 OS 中,哪怕只剩一块空闲,首次装入也算一次缺页中断,FIFO 的调度意义正是从那一刻开始的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











