考研也好,直接工作也好,只要你的方向是计算机相关,操作系统里的“虚拟存储器”这一章,就绝对绕不过去。我在备考那会儿,把这章反复啃了三遍,后来帮别人做面试模拟时发现,它同时也是技术面试的高频考点。很多同学觉得这一章概念多、算法杂、题目问法刁钻,但我可以负责任地说:只要你把“为什么需要虚拟存储”这条逻辑主线理清楚,再配合几个经典考点的标准答法,这章反而是性价比极高的一部分。
这篇总结我按自己的复习习惯来写,不按教材目录平铺,而是把最容易考、最容易混淆的点集中拆开讲。既照顾简答题,也把背后的原理讲透,目的是让你在考场上能写出“有深度”的答案。
1. 虚拟存储器的本质:它到底解决了什么问题
先说一个最基础、也最容易被忽略的问题:为什么需要虚拟存储器?
很多人背定义能背出来——“虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统”。但如果你只是背这句话,遇到稍微变通的简答题就会卡住。我建议你从实际场景去理解。
早期计算机的内存很小,一个程序要想运行,必须整个装进内存。程序一大了,内存放不下,怎么办?要么不让它运行,要么程序员手动把程序分成若干块,哪块要用再调进来。这显然很痛苦,而且人的精力根本没法应付复杂程序的分块调度。
后来人们发现了程序运行的局部性原理:程序在一段时间内,访问的指令和数据往往集中在某个局部区域。也就是说,程序运行时并不需要把全部代码和数据都放在内存里,只需要把当前要用到的那部分调入内存,其余部分放在外存(磁盘)中备用。这就是虚拟存储器的思想基础。
所以虚拟存储器解决的核心矛盾是:大程序跑在小内存上。它用时间换空间,用磁盘空间做内存的“后援”,配合中断机制和置换算法,在逻辑上给程序员提供一个几乎无限大的内存,物理上却只占用了很少的内存。
这里有个概念要分清楚:虚拟存储器是一个逻辑上的概念,它不是一块真实存在的存储设备,而是操作系统提供的一种存储管理方式。它建立在“基本分页/分段存储管理”的基础上,并在此基础上增加了请求调入和置换两大功能。
2. 核心机制拆解:请求分页与缺页中断
理解了“为什么”,接下来就要解决“怎么做”。虚拟存储器的具体实现主要依托请求分页或请求分段,其中请求分页是学习和考试的重点。
2.1 请求分页与基本分页的区别
基本分页存储管理,要求一个作业的所有页面一次性全部装入内存。而请求分页存储管理,则允许作业的部分页面装入内存即可开始运行,其他页面需要时才动态调入。
那么“需要时才动态调入”是如何实现的?关键在页表机制上。请求分页的页表项比基本分页要复杂,一般包含以下几个字段:
- 页号:逻辑页面的编号。
- 物理块号:该页面在内存中实际存放位置的块号。
- 状态位(存在位)P:指示该页面是否已调入内存。这个字段是判断是否发生缺页中断的依据。
- 访问字段 A:记录页面被访问的情况,供置换算法参考。
- 修改位 M:记录页面调入内存后是否被修改过。如果被修改过,换出时要写回外存;否则可以直接丢弃。
- 外存地址:该页面在外存中的存放位置。
可以看到,相比基本分页,请求分页多出的核心字段就是状态位、访问位和修改位。考试简答题常会问“请求分页与基本分页有何区别”,你要能准确答出这些字段的差异。
2.2 缺页中断的完整流程
缺页中断是整个虚拟存储器的“发动机”。它的处理过程必须烂熟于心,因为简答题和综合题都会涉及:
- CPU访问逻辑地址,硬件根据页号查询页表;
- 检查页表项中的状态位P,发现该页不在内存;
- 触发缺页中断,CPU切换到操作系统内核态;
- 操作系统检查内存中是否有空闲物理块:
- 若有空闲块,则根据外存地址将页面调入指定块;
- 若无空闲块,则按置换算法选择一个页面换出(若被修改过则写回外存),再调入新页面;
- 更新页表和内存中的块表,状态位改为1;
- 恢复进程执行,重新访问那条引发缺页的指令。
这里有个必须记住的细节:缺页中断在指令执行期间发生,而不是指令执行结束后。它有别于一般的外部中断,是内中断(异常)。一条指令可能引发多次缺页中断,比如一条指令同时访问指令和数据,而这两者都不在内存时。
2.3 地址变换过程
做题时我们常遇到地址变换的题目。请求分页的地址变换过程和基本分页很像,但多了一个“缺页处理”的岔路。我建议你把流程这样记:
- 逻辑地址 → 分离页号和页内偏移;
- 查页表,看该页是否在内存;
- 在内存:计算物理地址 = 物理块号 × 页大小 + 页内偏移;
- 不在内存:触发缺页中断,调入页面后重新执行。
实际考试中,若题目给出了页表、内存块大小、逻辑地址,要求你求物理地址或判断是否缺页,你按这个步骤操作即可,一步一步列出来,阅卷老师爱看。
3. 页面置换算法:考研简答题的主战场
页面置换算法是必考内容,而且不只是背定义,还要求会计算、会比较。我按考点热度从高到低给你梳理一遍。
3.1 最佳置换算法(OPT)
思想:选择以后永远不再使用、或在最长时间内不再被访问的页面换出。
这个算法是理论上的最优解,保证获得最低的缺页率。但它的问题是:操作系统无法预知进程未来的页面访问序列,所以实际无法实现。
那为什么还要学它?因为它是衡量其他算法优劣的标杆。考试中常以它的缺页次数作为参考值,与FIFO、LRU的结果对比。比如一道经典题:给一个页面访问序列,分别用OPT、FIFO、LRU计算缺页次数,问你哪个更接近最优。
3.2 先进先出算法(FIFO)
思想:优先换出最先进入内存的页面。可以用一个队列实现,谁先进来谁先出去。
这个算法的优点是实现简单,但性能并不好,而且有个著名的反常现象——Belady异常:物理块数增多时,缺页次数反而可能增加。
这个点极容易出简答题,你得能说清楚它为什么会这样:FIFO没有考虑到程序的局部性原理,换出的往往是当前频繁使用的“老页面”,导致刚换出去又被访问,引发频繁缺页。
3.3 最近最久未使用算法(LRU)
思想:选择最近一段时间最长时间没有被访问的页面换出。
它利用“最近的过去”预测“最近的将来”,认为过去一段时间没被访问的页面,将来一段时间也不太可能被访问。LRU的性能很接近OPT,且没有Belady异常。
实现上需要硬件支持,可借助栈或寄存器来记录页面访问时间。常见的有两种实现思路:
- 计时寄存器:每页关联一个计数器,页面被访问时清零,其他页面计数器加1,换出时找计数器最大的页面;
- 访问栈:每当页面被访问,就把它从栈中移到栈顶,栈底就是最久未被访问的页面。
考试中如果你要手算LRU的缺页次数,用“从后往前看”的技巧更快:当缺页需要置换时,看内存中已有的页面,从当前访问位置往前找,最后一次出现的页面就是被置换者。
3.4 时钟算法(Clock,又称NRU)
Clock算法是实际系统中应用最广泛的,因为LRU的实现开销大,而Clock是LRU的近似实现。
基本原理:为每页设置一个访问位,把内存中的页面组织成循环链表。页面被访问时,访问位置1。需要置换时,从指针当前位置开始扫描:
- 遇到访问位为0的页面,直接换出;
- 遇到访问位为1的页面,将其清零,继续扫描;
- 如果第一轮全部都是1,全部清零后指针回到起点,再次遇到第一个页面时换出。
两次机会算法是Clock的改进:增加一个修改位。组合起来有四种情况:
- (0,0):未被访问,未被修改,最佳换出对象;
- (0,1):未被访问,但被修改过,换出时需要写回外存;
- (1,0):被访问过,未修改;
- (1,1):被访问过,也被修改过。
扫描时优先选择(0,0),找不到再依次放宽条件,每轮扫描时都把访问位清零。这个改进能减少磁盘写回次数,提高系统效率。
3.5 各算法对比与记忆方法
你在答题时,最好能画一个小表格来说明各算法的优缺点。不过咱们文字总结,我直接把关键词给你:
| 算法 | 思想核心 | 优点 | 缺点 | 是否可实现 |
|---|---|---|---|---|
| OPT | 未来最久不被访问 | 缺页率最低 | 无法预知未来 | 不可实现,理论标杆 |
| FIFO | 最先进入先换出 | 实现简单 | 可能出现Belady异常 | 可实现 |
| LRU | 过去最久未被访问 | 性能接近OPT,无Belady异常 | 需要硬件支持,开销大 | 可实现但代价高 |
| Clock | 循环扫描访问位 | 开销小,兼顾性能 | 是LRU的近似,不一定最优 | 可实现,实际常用 |
记忆口诀:OPT看未来,LRU看历史,FIFO看进门顺序,Clock看访问标记。
4. 疑难知识点剖析:抖动、工作集与内存分配策略
这些概念是很多同学复习时的瓶颈,也是简答题中比较容易“丢分”的地方。因为它们不像置换算法那样有计算过程,更多需要理解和记忆。
4.1 什么是“抖动”?
抖动(Thrashing)是指系统中运行的程序过多,频繁发生缺页中断,使得系统大部分时间消耗在页面置换和磁盘I/O上,CPU利用率急剧下降的现象。
要理解抖动,可以想象一个图书馆自习室:座位(物理内存)有限,每个学生(进程)不时需要换一批书(页)放在桌上。如果同时学习的学生太多,座位不够,你刚换完书别人就要坐你的位置,你不得不把书搬走,频繁地搬进搬出,真正看书的时间反而极少。
抖动的后果是系统吞吐量骤降,甚至趋于瘫痪。解决办法主要有:
- 调整全局置换策略,限制进程的驻留集大小;
- 利用工作集模型,预先分配足够的物理块;
- 适当挂起部分进程,腾出内存给活跃进程。
考试常问“如何消除抖动”,你要能从进程调度和内存分配两个角度回答:一是降低进程数,让每个活跃进程有足够的物理块;二是采用局部置换策略,避免进程之间互相争抢页面。
4.2 工作集模型
工作集(Working Set)是指在某段时间间隔内,进程实际访问的页面集合。
工作集模型的核心用途是:确定每个进程需要多少个物理块,从而避免抖动。具体做法是,操作系统跟踪每个进程的工作集,确保分配给它的物理块数不小于工作集大小。如果所有进程的工作集之和大于物理内存容量,系统就会发生抖动,此时需要考虑挂起部分进程。
工作集的概念要区分清楚:工作集W(t, Δ),其中t是当前时刻,Δ是窗口大小。它表示在t-Δ到t这段时间内进程访问过的页面集合。窗口大小选择很关键,太大则工作集接近全部页面,失去预测意义;太小则不能反映真实的局部性。
4.3 驻留集与分配策略
驻留集(Resident Set)是指操作系统分配给一个进程的物理块集合,也就是进程当前驻留在内存中的页面数。
分配策略有两个维度:
第一个维度:物理块分配的数量,分固定和可变分配。
- 固定分配:进程在整个生命周期内物理块数量不变;
- 可变分配:系统根据缺页情况动态调整进程的物理块数量。
第二个维度:置换的范围,分全局和局部置换。
- 全局置换:允许进程从其他进程那里拿物理块,自己缺页时可以换出别的进程的页面;
- 局部置换:只能在自己的物理块范围内换出页面。
常见组合有三种:
- 固定分配局部置换:每个进程分固定数量的物理块,缺页时只能在自己块中置换。实现简单,但分配数量不好把握,少了频繁缺页,多了浪费内存;
- 可变分配全局置换:系统保留一个空闲物理块队列,进程缺页时从空闲队列取新块,空闲队列不够时换出任何进程的页面。这种方式最灵活,但一个进程可能过多占有物理块;
- 可变分配局部置换:允许动态增加或减少进程的物理块数,但置换只能在自己块内进行。折中方案,效果较好,但实现较复杂。
4.4 影响缺页次数的因素
还有一个常考的综合问题:影响缺页次数的因素有哪些?
从我的复习经验看,主要归纳为四点:
- 页面大小:页面划分过小,页表占用内存大,且缺页率上升;页面过大,页内碎片增加,局部性变差。需要折中考虑;
- 分配物理块数:分配给进程的物理块越多,缺页次数越少,但超过一定限度后效果不再显著;
- 页面置换算法:算法选得好,缺页次数就少;
- 程序编制的局部性:程序结构也会影响缺页。这里有个经典例子:循环嵌套的次序会影响页面访问顺序。比如一个二维数组按行存放,先按行循环访问,局部性好,缺页少;如果按列循环访问,跳着访问,局部性差,缺页会明显增多。
第四个因素在综合应用题里经常出现,出题人给你一段程序代码,问你改写循环次序能减少多少缺页次数。你要能识别出“按行访问优于按列访问”这个关键点。
5. 高频简答题整理:可以直接背诵的答题模板
备考冲刺阶段,简答题光靠理解不行,还得能快速、准确地在考场上写出来。我把自己整理过的高频题和答题思路分享给你,每个题都按“先给定义,再展开要点”的结构来写,这样既容易得分,也显得有逻辑。
5.1 什么是虚拟存储器?它的特征是什么?
答:虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。其逻辑容量由内存容量和外存容量之和决定。
特征有三个方面:
- 多次性:作业被分成多次调入内存运行。这是虚拟存储器最重要的特征。
- 对换性:作业运行过程中,允许将暂不使用的页面换出到外存,再调入需要的页面。
- 虚拟性:从逻辑上扩充内存容量,使用户感觉到的内存容量远大于实际物理内存容量。
注意答题时“多次性”一定要放在最前面,这是虚拟存储器区别于传统存储管理方式的本质特征。
5.2 实现虚拟存储器的条件是什么?
很多同学会漏掉这个题,但它考得并不少。标准答法:
- 必须有相当容量的外存(磁盘)作为后援,存放暂时不用的页面;
- 必须有一定容量的内存,容纳进程运行所需的当前活跃页面;
- 必须有请求调入功能,能在缺页时从外存调入页面;
- 必须有页面置换功能,内存满时能按策略换出页面并腾出空间。
这四条是递进关系,缺一不可。答题时最好把请求调入和置换功能分开说,突出它们是虚拟存储器的两大核心功能。
5.3 缺页中断与普通中断的区别
这个题高频,且容易答不全。区别主要有三点:
- 触发时机不同:缺页中断是在指令执行期间检测到页面不在内存时产生的,而普通中断一般在指令执行结束后响应;
- 重复执行:缺页中断处理后,CPU会重新执行引发缺页的那条指令,而普通中断处理结束后通常执行下一条指令;
- 次数不限定:一条指令在执行过程中可能触发多次缺页中断(如指令本身和数据都不在内存),而普通中断在一条指令执行期间通常只触发一次。
展开来说,缺页中断属于异常(内中断),它由CPU内部事件触发,与外部中断的区别也要能说出来。
5.4 页面置换过程的完整描述
出题人会问:“当内存空间不足时,页面置换的过程是怎样的?”答题模板如下:
- 检查内存中是否有空闲物理块,有则直接调入页面;
- 若没有空闲块,按适当的页面置换算法选择一个要换出的页面;
- 检查被换出页面的修改位,若为1(被修改过),则将该页面写回外存;
- 将被访问的页面从外存调入内存中的空闲物理块;
- 更新页表(修改状态位、物理块号等);
- 返回用户进程,重新执行刚才的指令。
5.5 Belady异常是什么?哪些算法存在?
答:Belady异常是指在页面置换中,分配的内存块数增加时,缺页次数反而增加的现象。FIFO算法可能出现Belady异常,而LRU和OPT算法不会出现。
这个题常以简答或填空题出现,注意强调“FIFO可能”而不是“FIFO一定”,因为在某些特定的页面访问序列下它也不会出现异常。
5.6 什么是局部性原理?它在虚拟存储器中有何作用?
局部性原理包括时间局部性和空间局部性:
- 时间局部性:如果某个数据或指令被访问,那么它在不久的将来可能再次被访问。这来源于程序中的循环结构。
- 空间局部性:如果某个数据或指令被访问,那么它附近的数据或指令也可能很快被访问。这来源于程序顺序执行的特点。
局部性原理是虚拟存储器能够实现的理论基础。有了它,我们才敢只把部分页面调入内存,就能保证程序在大多数情况下能以接近全内存驻留的效率运行。
6. 现场易错与面试实战心得
最后这个部分,写点我实际踩过坑,以及帮人答疑、模拟面试时发现的高频易错点。
6.1 易混淆概念自查清单
复习后期你不需要再从头翻书了,对照这份清单自检即可:
- 虚拟存储器 ≠ 虚拟地址空间:虚拟地址空间是地址抽象,虚拟存储器是存储管理方式。考试概念题最爱混淆这两个;
- 缺页中断 ≠ 页面置换:缺页中断是事件,页面置换是缺页中断处理中的一个环节。严格说,缺页了不一定发生置换(如果还有空闲块),必须先判断有没有空闲块;
- FIFO和LRU都可能缺页率高,但原因不同:FIFO不顾及访问频率,LRU关注最近访问时间而非访问频率。有个典型例子:一个页面被频繁但间隔很远地访问,LRU可能把它换出;
- Belady异常只讨论缺页次数增加,不讨论页错误率降低,别背反了。写答案时注意措辞;
- 工作集与驻留集不是一个东西:工作集是进程“需要”的页面集合,驻留集是系统“给”它的物理块集合。前者反映需求,后者反映供给。
6.2 面试中怎么答这个主题
面试和考研笔试不一样,面试官更看重“你是不是真的理解了”。如果你被问到虚拟存储器,一个比较好的回答思路是场景化:
你可以先抛一个场景:假如你的电脑运行着一个很大的应用,内存不够用,会发生什么?然后引出虚拟存储器解决的核心问题,再用“局部性原理”解释为什么可以这么干,最后落到“缺页中断”和“置换算法”这两个具体机制上。
面试官如果追问“LRU和Clock有什么区别”,你要能说出:LRU是理论最优近似,需要精确记录访问时间,成本高;Clock用访问位近似实现,成本低,是实际系统中的折中。
6.3 复习建议与踩坑提示
我个人的备考路径是这样的:先建框架,把“为什么需要虚拟存储”想明白;再逐个击破细节;最后反过来用真题检验。
具体建议有三点:
第一,手算置换算法一定要画表格。不要心算,考试时间再紧也要画。表格的横轴是访问序列,纵轴是内存块状态,这样不容易乱,而且阅卷时一目了然。
第二,不要死记硬背答案。简答题的得分点在于关键词,比如“多次性”、“请求调入”、“置换功能”、“状态位”、“修改位”这些术语必须准确出现,但具体怎么组织句子,你可以用自己的话。
第三,注意和前面的分页、分段内容串起来。第五章虚拟存储器并不是独立的,它建立在前面几章的基础上。如果让你分析请求分段虚拟存储器的换入换出,原理和请求分页几乎相同,只是把“页”换成“段”,把“页表”换成“段表”。举一反三的能力在考试中很重要。
6.4 分享两个小技巧
每次模拟考我都会犯的错,就是漏答“修改位导致写回外存”这个环节。后来我给自己编了个口诀:查状态、找空闲、选置换、看修改、调页面、改页表。六个步骤对应缺页中断处理的完整流程,按这个顺序答,永远不会漏点。
第二个技巧是应对“为什么抖动后CPU利用率会下降”的追问。大家容易直觉认为缺页多了,CPU总该忙着调页吧,为什么利用率低?因为调页是磁盘I/O操作,此时CPU是空闲等待状态;而大量进程同时进入缺页等待,调度器来回切换,CPU真正干活的比率就下来了。答题时点出“CPU在等磁盘I/O”这层意思,就显得你的理解上了一个台阶。
虚拟存储器这部分内容,知识点密度高,但逻辑链非常清晰。从局部性原理出发,到请求调入与置换两大核心功能,再到各类置换算法的比较,最后落到抖动与工作集等系统级问题,每一个环节都能用“为什么”串起来。备考时多问自己几个为什么,把每个概念放到这个逻辑链条里去理解,答题时就不会只停留在背诵层面。