FIFO页面置换仅用std::queue必然出错,因其无O(1)查找能力,导致缺页误判、命中率虚高、淘汰错位;正确做法是std::queue管理入序,std::unordered_set同步维护驻留页集,二者操作严格对齐。

FIFO 页面置换不能只靠 std::queue 就跑通——缺页判断失效、命中率虚高、淘汰错位,几乎必然发生。
为什么 std::queue 单独用会算错命中率
因为 std::queue 只管进出顺序,不提供 O(1) 查找能力。每次访问页面前必须快速判断该页是否已在内存中,否则:
- 本该命中的访问被当成缺页,重复装入同一页面 →
pageFaults虚高 - 没清掉旧页在驻留集合里的标记 → 后续再访该页仍报缺页
- 用
queue::front()淘汰时,若队列已空或未同步更新驻留状态,可能淘汰一个根本不在内存里的页号
正确做法是:用 std::queue 管“进入顺序”,用 std::unordered_set(或 std::vector)单独维护“当前哪些页在内存里”。二者操作必须严格同步:装入时 push + insert,淘汰时 pop + erase,命中时只查 set 不动 queue。
std::queue + std::unordered_set 同步操作要点
关键不是“能不能用”,而是“怎么保证每一步都对齐”。常见错误代码往往漏掉其中一环:
- 装入新页时:
q.push(page)✅ 但忘了in_memory.insert(page)❌ - 淘汰旧页时:
int old = q.front(); q.pop();✅ 但没写in_memory.erase(old)❌ - 命中判断写成
if (q.find(page) != q.end())——std::queue根本没有find成员函数,编译不过 - 改用遍历
std::queue判断存在性 → 时间复杂度退化为 O(m),m 是物理块数,m=64 时单次判断就慢 64 倍
示例片段(核心逻辑):
if (in_memory.find(page) != in_memory.end()) {
// 命中:不改动 queue,不增 pageFaults
} else {
// 缺页
pageFaults++;
if (q.size() == frame_count) {
int old = q.front();
q.pop();
in_memory.erase(old); // 必须删
}
q.push(page);
in_memory.insert(page); // 必须加
}
循环数组实现 FIFO 时的下标陷阱
不用 STL、手写循环队列更易暴露边界问题。常见崩坏点:
-
cur初始化为 0 后,没做cur = cur % frame_count防越界 → 内存越界写入相邻变量 - 淘汰位置计算写成
frames[cur],但没在装满后才开始移动cur→ 前frame_count次访问就把cur提前移走了,导致最早装入的页没被优先淘汰 - 数组分配大小写成
new int[frame_count],但循环逻辑用了(cur + 1) % frame_count→ 实际需要至少frame_count个槽位,没问题;但如果误写成new int[frame_count - 1],取模后下标仍可能越界
安全写法:分配 frame_count 元素,cur 仅在 size == frame_count 且发生缺页时才更新:cur = (cur + 1) % frame_count。
命中率分母必须是总访问次数,不是“有效访问”或“装入次数”
这是实操中最常被悄悄篡改的逻辑。例如序列 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 共 17 次访问,分母就是 17:
- 第 1 次访问 7 → 缺页,计入分子
- 第 5 次又访问 0 → 若已在内存,命中,不计入分子,但计入分母
- 有人把“内存未满前的访问”跳过统计 → 分母变成 14 或更小,命中率失真
- 有人把“首次装入”和“后续命中”都当“一次调度事件”去计 → 分子变大,命中率虚低
操作系统真实行为:只要触发了页面访问,无论内存是否为空、无论是否命中,都是一次有效访问。缺页率 = pageFaults / total_accesses,不可约简、不可过滤。
真正难的不是写完一个能跑的 FIFO,而是让 in_memory 集合、queue(或循环数组)、pageFaults 计数器三者在每一次访问后始终严格一致——差一行同步,命中率就偏 5% 以上,而这个偏差在小规模测试里根本看不出来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











