ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

操作系统虚拟内存考点精讲:页面置换算法与有效访问时间

操作系统虚拟内存考点精讲:页面置换算法与有效访问时间 先说明一件事被大家叫了很多年的“恐龙书”《操作系统概念》十版之后它的结构已经非常稳定了。第10章基本固定为虚拟内存Virtual Memory英文版叫 Chapter 10中文版也大多译成“虚拟内存”。很多同学找“第10章课后答案”其实并不是缺一份标准答案而是这一章的计算和简答题光看答案根本记不住思路有效访问时间怎么代公式、页面置换怎么手算、缺页率为什么会影响整个系统性能。这篇文章不谈那些“照着抄就行”的答案我把带学生复习时最常用的解题框架、完整算例和最容易错的细节整理出来你对着做题会比直接翻答案有效得多。1. 这一章到底在考什么1.1 虚拟内存解决的核心矛盾操作系统的内存管理前面几章讲连续分配、分段、分页解决的是“进程怎么放在物理内存里”的问题。到了虚拟内存这一章问题升级了物理内存就那么点可同时跑的进程又那么多怎么让每个进程都觉得“我拥有很大、很快、很连续的内存”虚拟内存把逻辑地址空间和物理地址空间拆开进程的地址空间可以大于物理内存也可以被多个进程共享。其中按需分页demand paging是整章的起点进程启动时并不把所有页面全部载入内存而是用到哪一页再调进哪一页。这个概念说起来简单但它带来的连锁反应非常多比如页面置换算法怎么选、缺页率怎么控制、内存会不会抖动。课后题里大量题目本质都是在问在“内存不够页面按需加载”的前提下系统性能到底怎么变。备考时我建议你把这句话写在笔记本第一行虚拟内存的核心是“把物理内存当成进程工作集的缓存”。有了这个视角后面算缺页率、理解抖动都顺了。1.2 按需分页与缺页中断的完整链路看书上流程图很容易但考试喜欢让你手写“一次缺页中断发生后的处理过程”。标准链路是这样的CPU访问某个逻辑地址MMU查页表页表项里的有效位valid-invalid bit显示该页不在内存MMU触发缺页中断CPU陷入操作系统OS检查进程内部表确认这次缺页是“合法缺页”而不是“非法地址”在磁盘上定位该页的位置选择一个空闲物理帧发起磁盘I/O把页面读入内存更新页表项把有效位改掉同时记录该页与物理帧的映射关系重新执行导致缺页的那条指令。这里最容易被考题抓住的是最后一步。为什么一定要“重新执行指令”因为一条指令可能跨过多个页面前半段在页A后半段在页B如果你的第一个操作数触发了缺页重新执行时不能只从“缺页那一刻”继续必须整个指令重来。所以硬件在设计时就要保证指令是可重启的。课后有题目会反过来问“缺页中断如果处理得太慢会发生什么”答案同样围绕这条链路页面调入期间CPU被阻塞或切换出去系统吞吐率降低如果缺页率高到一定程度CPU大多数时间在等磁盘系统响应就会明显变差。1.3 页表项里那些“送分位”页表项本身也是一个高频考点因为它直接关系到页面置换算法的实现。常见位包括有效位、脏位modify bit、访问位reference bit、保护位等。有效位表示该页在不在内存。缺页时本质就是这个位为0。脏位表示该页调入内存后有没有被写过。置换时如果脏位为0说明页面内容在磁盘上还有一份干净副本可以直接丢掉不用写回磁盘如果脏位为1则必须先把内容写回磁盘才能释放帧。这个细节经常出现在“提高页面置换性能”的讨论题里。访问位用于LRU近似算法。硬件在每次访问页面时把该位置1操作系统周期性清零用来估计页面最近有没有被用过。简答题里会问“为什么需要脏位”答题时要把“写回磁盘的I/O开销”点出来。我见过不少学生答成“为了保存修改内容”这个也没错但不够完整标准答案的逻辑是脏位决定了换出页面时是否需要额外的磁盘写操作这是置换代价差异的核心。2. 必考计算题之一有效访问时间2.1 公式的来龙去脉有效访问时间Effective Access TimeEAT是第10章第一类能直接套公式的计算题。公式本身很简单但你要理解每一项怎么来的否则题目稍微一变就懵。设ma为一次内存访问时间也就是内存延迟p为缺页率即“引用一个页面时发现它不在内存”的概率F为缺页处理时间通常包含磁盘寻道、旋转延迟、传输时间以及缺页后的重新执行指令时间。一次内存访问有1-p的概率正常命中用时ma有p的概率发生缺页总用时接近ma F但缺页处理那一大段时间已经远超一次普通访问实际计算中常常写成EAT (1 - p) × ma p × F注意有些教材会把缺页时“还要先访问一次内存才知道缺页”也写进去但那部分时间太小通常忽略。做题时先看题目有没有单独提“页表访问时间”如果没有就直接用上面的简化公式如果有要按题目把页表访问的额外次数加进去。2.2 一道完整的真题风格例题拿一道经常在作业里出现的题来演示。设内存访问时间ma 200 ns缺页服务时间F 8 ms包含磁盘读入、可能写回、更新页表、重新执行指令缺页率p 1/10000先统一单位8 ms 8,000,000 ns。代入公式EAT (1 - 0.0001) × 200 0.0001 × 8,000,000EAT 0.9999 × 200 0.0001 × 8,000,000EAT 199.98 800 999.98 ns结果大约是1000 ns。这个例子特别能说明问题缺页率只有万分之一看起来很低但有效访问时间是正常访问时间200ns的5倍。原因很简单缺页服务时间实在太大把平均时间彻底拖垮了。考试里如果题目把缺页率提高到1/1000EAT会变成约800 199.8 ≈ 1000不对0.001 × 8,000,000 8000加199.8约8200ns也就是正常访问的41倍性能直接崩。这也就是为什么操作系统设计的目标不是“尽量少缺页”而是“把缺页率压到极低”。任何导致缺页率上升到千分之一级别的算法在实际系统中都是灾难。2.3 两个容易掰扯不清的边界条件第一缺页服务时间到底要不要包含“换出页面的写回时间”标准答案里通常分两种情况如果是置换算法引入的缺页并且被置换页是脏页那F里要包含写回时间如果是刚刚启动、有空闲帧可用的缺页F里不包含换出时间。你看课后题有没有给定“脏页比例”或者“被置换页平均需要写回”的条件不要自己脑补。第二题目问你“缺页率必须小于多少才能让EAT不超过某个值”时本质上是在解不等式。比如我要求EAT不超过400 ns那就是(1 - p) × 200 p × 8,000,000 400解出来p × (8,000,000 - 200) 200p 200 / 7,999,800 ≈ 0.000025也就是缺页率不能高于2.5 × 10^-5。这种变形题每年都有人因为单位没换算错掉算之前先把所有时间统一成ns。3. 必考计算题之二页面置换算法手算3.1 先统一做题的“记法”页面置换算法是第10章计算题的大头FIFO、LRU、OPT最优置换这三件套几乎每次作业都出现。手算时我最推荐的方法不是背结论而是画“帧状态”表每一列是一次引用每一行是一个物理帧表格里的值是当前留在帧里的页面号。每遇到一个新引用先看它是否已经在任何一个帧里在就写“命中”不在就要按算法选一页踢掉。这里有个特别容易混淆的约定缺页次数到底算不算“进程最开始页面都不在内存”的那几次一般来说要算。教科书上的缺页率都是把一个空帧集合作为初始状态所以第一次引用一定会缺页。有些课后题会额外说明“所有页已经预装在内存”那种情况第一轮引用就不算缺页。做题前先看清楚题干。另一个约定是缺页中断时如果系统还有空闲帧就不会触发置换。课后题通常默认帧数固定且都满了这样才考察置换算法的选择逻辑。3.2 FIFO、LRU、OPT同台竞技用教材和作业里最常出现的一条经典引用串来演示7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1设系统只有3个物理帧分别用FIFO、LRU、OPT计算缺页次数。先把FIFO结果完整推一遍。内存帧集合用“先装入 - 后装入”的顺序表示7缺页帧[7]0缺页帧[7,0]1缺页帧[7,0,1]2缺页淘汰最老的7帧[2,0,1]0命中3缺页淘汰第二老的0帧[2,3,1]0缺页淘汰最老的1帧[2,3,0]4缺页淘汰最老的2帧[4,3,0]2缺页淘汰最老的3帧[4,2,0]3缺页淘汰最老的4帧[3,2,0]0命中3命中2命中1缺页淘汰最老的3帧[1,2,0]2命中0命中1命中7缺页淘汰最老的1帧[7,2,0]0命中1缺页淘汰最老的2帧[7,1,0]FIFO最终缺页次数是15次。LRU按最近最久未使用来淘汰。关键区别在于命中某个页面后要把它标记为“最近刚被用”下次淘汰时它就不会第一个被踢。按同样引用串手算缺页位置大致是第1、2、3、4、6、8、9、10、11、14、16、18位总次数12次。为什么比FIFO少因为LRU能保留即将再次访问的热点页而FIFO只看装入顺序不看访问频率。OPT算法要往后看当缺页需要淘汰一个页面时选择那些在未来最长时间内不会被用到的页面。这个算法手算时比较费眼睛但结果最漂亮缺页次数只有9次。OPT的意义不是真的要你实现未来访问序列不可能提前预知而是作为衡量算法优劣的下界。LRU能逼近这个下界FIFO则差得多。算法缺页次数FIFO15LRU12OPT9考场上手算时我建议先列引用串再画帧状态表格。别在脑子里推容易乱。3.3 Belady异常为什么帧数多了反而更差教科书都要讲Belady异常这是个反直觉现象对某些引用串增加物理帧数反而导致缺页次数增加。Belady异常只在FIFO这类“看不出未来、也不看最近访问”的算法里出现LRU和OPT不会。经典例子是引用串1 2 3 4 1 2 5 1 2 3 4 5。用3个帧跑FIFO缺页9次。用4个帧跑FIFO反而缺页10次。刚算的时候很多学生第一反应是不信。增加一帧本该减少缺页怎么反而变严重了原因在于FIFO牺牲页完全不看访问历史多出来的帧反而让某些早期页面更晚被淘汰导致本来可以避免的缺页在更长窗口上反复发生。考试如果考这个除了算次数还要能解释为什么LRU和OPT不会出现Belady异常LRU使用“过去访问时间”作为排序依据OPT使用“未来访问时间”作为排序依据两种依据都具备某种单调性而FIFO只是放页顺序与被访问的时序完全脱节。这种“一句话解释原理”的问法比单纯计算结果更能拉开分。3.4 LRU的近似实现也是热门考点纯LRU需要记录每个页面最近一次被访问的时间每次缺页时还要在这些时间戳里找最小值代价太大。所以实际系统从不使用严格的LRU而是用近似算法。最常见的题目是“第二次机会算法”和“增强型第二次机会算法”。第二次机会的思想把页面组织成环形队列像时钟一样扫描。每扫描到一个页面如果它的引用位是0就淘汰它如果引用位是1就把引用位置0并给这个页面第二次机会。做题时要注意指针移动方向和被访问页面的位置。增强型第二次机会把脏位也放进来形成四个优先级引用位脏位含义换出优先级00既没被访问也没被修改最优先换出01没被访问过但被修改过次优先需要写回10被访问过但没被修改再次11被访问过也被修改过最后换出这类算法的作业题一般不用算很多数字而是考“设计思想”和“与LRU的差距”。答题思路要落在用少量硬件位近似“最近使用”的概念牺牲一定准确性换取可实现的维护成本。4. 简答题的答题框架帧分配、抖动与内核4.1 帧分配的三种策略与判断页面置换的前提是“多个进程竞争物理帧”所以帧怎么分也是课后简答题常客。三种基本策略平均分配帧数按进程数均分比如系统有100个帧5个进程就各20个。优点实现简单缺点没考虑进程大小小进程用不完大进程不够用。比例分配按进程的虚拟地址空间大小给定权重进程越大分到的帧越多。公式常见为a_i (S_i / S) × m其中S_i是进程i的总页数S是所有进程页数之和m是可用帧总数。优先级分配把帧数分成高优先级和普通优先级两部分高优先级进程可以使用普通进程的帧。适合实时性要求不同的场景。这块问答题的价值不在于背公式而在于理解分配策略与全局置换、局部置换的关系。全局置换允许一个进程抢占其他进程的帧局部置换只允许进程在自己的帧范围内置换。如果题目问“为什么局部置换能降低抖动的影响”要答它隔离了进程之间的内存争用某个进程缺页增多不会把其他进程的帧也拖下水。4.2 抖动、工作集与系统颠簸的判定抖动thrashing几乎是每年必考的名词解释。抖动的定义是进程频繁缺页导致大部分时间消耗在页面换入换出上而不是真正执行指令CPU利用率骤降。恶性循环是这样的系统发现CPU利用率低 - 以为多道程序度不够 - 增加更多进程 - 每个进程分到的帧更少 - 缺页更严重 - 系统更加忙于换页 - CPU利用率继续下降。答题时最好把这条因果链写完整只写“频繁缺页”拿不到全分。解决方法之一是工作集模型。工作集指进程在最近一段时间内实际访问过的页面集合窗口大小常用Δ表示。设每个进程的工作集大小为WSS_i系统所有工作集之和为D Σ WSS_iD表示系统当前真正需要的帧数。当D大于可用帧总数m时说明内存已经不够用了OS不是继续加进程而是应该挂起或让某些进程退出。这个模型把抖动的判断落到一个可计算的量上所以简答题很喜欢让你“给出一种检测抖动的方法”工作集检查就是标准答法之一。4.3 内存映射文件与内核内存分配第10章后半部分有两个看起来不难、但简答题很爱考的知识点内存映射文件memory-mapped files和内核内存分配。内存映射文件的本质把磁盘文件映射到进程地址空间的一部分访问这个地址范围就等同于访问文件内容。刚开始读到页面时才从磁盘载入后续修改页面会在内核的虚拟内存管理下写回文件。这个机制的好处是所有普通文件I/O都可以复用缺页和页面置换机制还天然支持进程间共享。共享内存shared memory也依赖这个概念。比如父进程创建子进程后如果使用mmap建立共享区域子进程对这块区域的修改父进程立刻可见而不需要显式的read/write系统调用。内核内存分配与用户内存分配不同内核需要小块内存还要求对齐。伙伴系统buddy system把连续内存按2的幂切块分配回收都比较快slab分配器则基于对象缓存每种内核对象比如进程描述符、文件对象有专门的slab缓存避免频繁分配和释放同一对象带来的碎片和开销。答题时注意说清“为什么要为内核单独设计分配机制”内核内存通常连续、对性能敏感、分配请求频繁而零碎通用页分配器不能满足。5. 课后作业中最容易踩的坑5.1 概念混淆坑第一个坑是把“虚拟内存大小”和“物理内存大小”混为一谈。虚拟内存的容量上限由地址位数决定比如32位系统理论上每个进程有4GB地址空间物理内存是实际插在机器上的内存。题目问“为什么虚拟内存容量可以大于物理内存”就是因为进程可以让大量页面驻留在磁盘只有用到时才调入。第二个坑是分不清“有效位”和“页表项里的保护位”。有效位回答“页在不在内存”保护位回答“能不能读/写/执行”。缺页是有效位为0不是保护位的问题。如果题目说“进程试图写一个只读页”那是保护违规不会触发缺页。第三个坑是算EAT时把“访问页表”和“访问数据”算成同一次内存访问。基础分页模型下一次数据访问要先访问页表再访问数据总共两次内存访问如果题目没有提TLBEAT里应该体现这一点。当然大部分课后题为了简化会默认TLB命中率很高或直接给你内存访问时间但你自己要清楚题目到底简化了什么。5.2 计算过程坑页面置换手算最大的坑是“只记名字不会画表”。有同学背住“LRU是最近最久未使用”但真动笔时却按FIFO的顺序踢页面因为不习惯维护“最近使用顺序”这个信息。我建议手算时从左往右扫引用串每处理一个引用就更新一次帧表同时用箭头标出最近使用的排序这样虽然慢一点但不容易错。另一个高频错误是单位换算。.缺页服务时间动辄毫秒级内存访问是纳秒级两者差三到四个数量级。很多人公式写对了最后数值错在把8ms直接当8然后和200ns相加。统一单位后再计算能救回不少冤枉分。最后一个问题是重复计数。有些同学在算“置换算法缺页次数”时把“该页第一次调入内存”和“该页后续被换出又换入”都算了一遍这没问题但他会把“命中”也误记为一次缺页。注意“缺页”不等于“页面被访问”只要页面那一刻已经在某个帧里无论它之后会不会被淘汰这次访问都算命中。5.3 复习方法坑我也见过不少学生把大量时间花在背课后答案上结果换一道题就卡住。第10章的计算题类型一共就那么几种不值得背真正该练的是“拿到引用串三分钟之内把某一算法的帧状态表画出来”。画表训练有个小技巧先不要追求算得快先用带播放的模拟器或自己手写表格把每一步都写下来算完几道题后你会形成肌肉记忆。我辅导时发现能顺畅写出FIFO/LRU完整轨迹的同学考试丢分率明显低于只记结论的同学。还有一个常见误区忽略“局部性与工作集”之间的联系。虚拟内存之所以能在“按需分页”的前提下维持不高的缺页率靠的就是程序的时间局部性和空间局部性。复习这一节时把工作集、抖动、分配策略放到一条逻辑链里理解局部性好工作集小帧分配足够系统不会抖动。单纯背名词解释会越背越乱。6. 如何正确使用CH10课后答案6.1 先独立做再看答案很多同学的复习节奏是“拿到课后答案先看答案再假装自己会了”。这样效率极低。道理很简单第10章的坑大多藏在细节里比如有效位和脏位的区别、EAT单位换算、置换算法的比较条件。你要是没先踩一遍看答案时根本不知道那道题的考点在哪里。我会强迫自己先做一遍哪怕错得很离谱。做错之后再看答案印象会深很多。尤其是页面置换题我会把自己的帧状态表和标准答案对齐从第一次缺页开始逐列对比看到底是哪一步选错了牺牲页。6.2 参考答案时重点看“判定逻辑”标准答案里的数值是结果判据才是重点。FIFO的判据是“最早装入的页面”LRU的判据是“最久没有被访问的页面”OPT的判据是“以后最长时间不会用到的页面”。三者看起来像但手算时每一步的决策依据完全不同。对答案时我会问自己三个问题这条引用串里页面第一次进入帧集合算不算缺页某个页面命中后算法会不会改变这个页面的淘汰优先级缺页时如果多个帧都可以牺牲我的选择是不是按算法判据唯一确定的这三个问题能覆盖大部分丢分点。如果你能对每一个都给出理由那这道题才算真正吃透。6.3 把这一章的题和后续内容串起来第10章不是孤立的虚拟内存理论。后面章节里的文件系统缓存、网络协议栈的缓冲区分配、数据库缓冲池管理本质上都在用同一套“缺页 置换 工作集”的思路。做课后题时多问一句“这套算法如果放在真实系统里谁来实现它”会帮你更好地理解操作系统中“硬件提供机制、操作系统提供策略”的分工。举个例子MMU负责在页表上查找地址并触发缺页中断但选择哪一页置换是操作系统的事硬件可以帮忙置引用位和脏位但要不要清零引用位、多久扫描一次是OS的策略。第10章很多简答题的答题边界就是围绕这个分工展开的。我个人复习这章时还发现一个很有效的做法每次做完置换算法题就顺手想一想“如果这个引用串代表真实程序的某段时间程序为什么会在这些地址之间跳来跳去”。一开始可能会觉得多余但练习几道题之后缺页率、工作集、抖动这些概念会慢慢从公式变成直觉。恐龙书的第10章说难也难说简单也简单。难在概念多、计算容易手滑简单在题型非常固定只要肯花功夫画几张帧状态表把有效访问时间公式吃透考场上拿分并不难。答案书只是用来验证对错的坐标真正带你通过这门课的是你自己动手算的那二十次缺页。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进