Skip to content

虚拟内存基本概念

2026 大纲 三(二)1 虚拟内存的基本概念

地址变换的问题解决了,但内存还是不够

分页、分段、段页式都在解决同一个问题:进程散着放,地址怎么翻译。 这个问题现在解决得很彻底了——外碎片没了,共享保护也有了, 剩下的开销靠 TLB 和多级页表也压了下来。

可它们全都没碰另一个更硬的限制:进程还是必须整个装进内存才能跑

这条限制卡死了两件事。单个进程不能比内存大——内存 2 GB, 就跑不了一个需要 3 GB 的程序,无论散着放还是连着放都一样。 并发度也被卡住——每个进程都要全额占着内存,装下几个就只能跑几个。

而与此同时,内存里堆着大量根本没在用的东西:错误处理代码可能一次也不执行, 一个巨大的数组可能只用了开头几百个元素。"必须全部装入"这个要求本身就是浪费的。

于是想法很自然:只把眼下用得着的那部分装进来,用到别的再去取。

但这个想法能不能成立,取决于一件不那么显然的事——程序访问内存的位置, 必须是扎堆的而不是均匀撒开的。如果每条指令都随机访问一个新页面, "用到别的再去取"就意味着几乎每次访存都要读一次磁盘,慢上几万倍, 这套方案当场破产。

好在真实程序确实扎堆,这就是局部性原理它是一个经验事实,不是逻辑必然—— 虚拟内存的全部可行性建立在它上面。所以这一节从一个反例讲起: 局部性不成立的时候,会有多惨。

一、局部性不成立会怎样:一个反例

推导链的每一环都靠局部性撑着,把这个前提抽掉,结论立刻反向。

假设有一个程序,每次访存都均匀随机落在整个地址空间的任意一页上(例如对一个远大于内存的表做完全随机的散列查找)。设驻留内存的页面只占全部页面的比例 r,则每次访存命中的概率就是 r缺页率 f=1r。取 tmem=100ns、一次缺页处理 tfault=5ms

情形缺页率 fEAT=(1f)tmem+ftfault相当于内存访问的多少倍
无局部性,驻留 50%0.52 500 050 ns ≈ 2.5 ms25 000 倍
无局部性,驻留 10%0.94 500 010 ns ≈ 4.5 ms45 000 倍
有局部性(典型)105150 ns1.5 倍

前两行意味着:没有局部性,虚拟内存会退化成"用磁盘当内存",慢到完全不可用。这也解释了为什么后面所有置换算法(LRU、CLOCK)都在赌局部性,以及为什么一个访问模式反常的程序会把系统拖进抖动

二、虚拟内存是什么

虚拟内存让每个进程拥有一个很大的、连续的虚拟地址空间,实际上只有一部分页面在物理内存中,其余存放在外存(磁盘)上。

虚拟地址空间内部怎么划分(代码段、数据段、堆向上长、栈向下长、中间留空洞)不属于本节,见运行时内存映像与地址空间。本节只关心一件事:这个空间里的页可以不全在内存

三个特征为什么是一条链而不是三个并列项(想弄清多次性、对换性、虚拟性谁是手段谁是目的时展开)
特征说明是手段还是目的
多次性程序不需要一次全部装入,可以分多次调入内存手段
对换性暂时不用的部分可以换出到外存,需要时再换入手段
虚拟性从逻辑上扩充了内存容量,用户感觉内存比实际大得多目的

把它们当并列的三条去背,就说不清"为什么必须三条都有"。实际上它们是一条推进链:

多次性解决"进得来"——不必等全部装完就能开始跑;但只有多次性还不够,内存迟早会被填满,装到第 k 批就走不动了。

对换性解决"出得去"——把暂时不用的部分换到外存,腾出空间给新的页面,于是"分多次调入"这件事可以无限进行下去,而不是只能进行有限的几轮。

两者合起来,进程实际用到的地址空间就不再受物理内存限制 ⇒ 这才产生了虚拟性

判据:多次性和对换性是可观察的机制,虚拟性是用户感受到的效果。 所以虚拟性是最本质的特征——去掉它,前两者就没有存在的理由;反过来,缺了前两者中的任何一个,虚拟性都产生不出来。

三、三项硬件支持,按缺口顺序推出来

缺口一:怎么知道某一页在不在内存?页表机制。基本分页的页表项只有"页号 → 页框号",无法表达"这一页不在内存"这种状态,于是扩出四个字段:

新增字段补上的缺口
状态位 P回答"在不在内存"——地址变换时先查它
外存地址回答"不在内存的话,去磁盘的哪个块取"
修改位 M回答"换出时要不要写回"(M=0 可直接丢弃,省一次磁盘写)
访问字段 A回答"置换算法该淘汰谁"

缺口二:发现 P=0 时,怎么把控制权交给 OS,处理完还能接着算?中断机制。这里要求的不是普通中断:它必须能在指令执行期间触发,且保存的现场要足以让这条指令从头重新执行。满足这两条的正是内中断里的故障(Fault)类——它的返回语义就是"修复后重新执行当前指令"。详见中断和异常的处理

缺口三:调入之后,怎么接着完成这次地址变换?地址变换机构。基本分页的变换是一条直路(拆页号 → 查表 → 拼物理地址),请求分页要在中间插一条回路:查到 P=0 就转去缺页中断处理,调入页面、更新页表和快表,再回到起点重新变换一次。所以它不只是"多查一位",而是流程本身多了一条分支和一次重入。

四、请求分段比请求分页多了什么

字段作用与请求分页对应吗
存取方式按段的逻辑属性做保护(只执行 / 只读 / 读写)请求分页没有——段是逻辑单位才谈得上属性
访问字段 A供置换算法参考对应
修改位 M换出时要不要写回对应
存在位 P该段是否已调入内存对应
增补位本段运行中是否做过动态增长请求分段特有——段长可变,栈段、堆段会长大
外存始址该段在外存的起始盘块号对应

缺段中断与缺页中断的机制一致(都在指令执行期间产生、一条指令可能触发多次),差别只在"跨块"这一条:段是信息的逻辑单位,一条指令不会被切到两个段里;页则可能跨页。这条差别的直接后果,就是两者对"最少需要几个存储块"的要求不同——见页框分配与回收

五、虚拟内存 vs 基本分页

比较基本分页请求分页(虚拟内存)
装入方式一次性全部装入按需调入(缺页时调入)
是否需要全部在内存
页表项页号 → 页框号增加状态位 P、访问字段 A、修改位 M、外存地址
缺页处理无此概念触发缺页中断(内中断·故障类),调入后重新执行该指令
页面置换内存满时需要置换
逻辑地址空间上限≤ 物理内存≤ min(内存+外存, 地址位数决定的寻址空间)

六、一次访存里,硬件、OS、编译器各干了什么

虚存的运转是三方分工的,题目会挑其中一环问"这一步归谁"。分工原则只有一句: 简单且每次访存都要做的交硬件,复杂但罕见的交 OS,跟运行期状态无关的才轮到编译器。

环节归谁为什么是它
生成逻辑(虚拟)地址编译器 / 链接器这是静态的事,与运行时装到哪无关
分配物理页框、填写页表项操作系统页框归谁要看运行时的全局状态,编译器无从知道
地址转换(虚地址 → 物理地址)硬件 MMU + TLB每条访存指令都要做一次,交给软件的话每次访存都得跑一段程序
检测缺页、越界、越权硬件检查和地址变换在同一条流水线上,顺手就做了
处理缺页与其他异常操作系统要找空闲页框、选淘汰页、读磁盘、改页表——全是复杂决策,且发生频率低

四处最容易答反的:地址转换不是 OS 做的(是 MMU);页表项不是编译器填的(是 OS); 缺页中断不是硬件"处理"的(硬件只负责触发,处理是 OS); 而异常一律由 OS 处理——缺页、越权、非法指令、除零都遵循 "硬件检测 → 触发异常 → OS 处理"这同一个模式。

考点速记

  1. 虚拟内存要打破的前提是"进程必须全部装入内存",它卡死了单进程规模与并发度;而进程里有大量很少甚至从不执行的代码全程占着内存。
  2. 理论基础是局部性原理时间局部性(刚访问过的很快还会访问)+ 空间局部性(刚访问位置的附近很快也会访问)。⚠️ 落点是"平摊下来可接受",不是"没有代价"——可行性建立在"真实程序有局部性"这个经验事实上,不是逻辑必然
  3. 三个特征是一条链多次性(进得来)+ 对换性(出得去)⇒ 才产生虚拟性(目的,也是最本质的特征)。前两者是机制,虚拟性是效果。
  4. 三项硬件支持各补一个缺口页表机制负责"知道"(状态位 P、外存地址、修改位 M、访问字段 A)、中断机制负责"救援"、地址变换机构负责"重来"。
  5. 缺页中断必须是故障(Fault)类内中断。普通中断在指令执行完毕后才检测、返回执行下一条;缺页是指令执行到一半(正取操作数)才发现的,那条指令根本没做完,处理完必须重新执行它
  6. 虚拟内存必须建立在离散分配之上。连续分配要求整个进程占一整块连续物理空间,"部分装入"在它那里根本不成立。
  7. 容量上限 =min( 内存 + 外存总容量,地址位数决定的寻址空间 ) 所以"虚拟内存 = 外存大小"是错的——32 位地址决定单进程寻址上限 4 GB,哪怕装 1 TB 硬盘也不会超过 4 GB。
  8. 软硬件分工:地址转换归硬件 MMU,页表项由OS填写(不是编译器),缺页由硬件检测、OS 处理一切异常都由 OS 处理
  9. 三种实现里请求分页最常用(换入换出单位定长);请求分段特有存取方式与增补位只有分页会跨块——段是信息的逻辑单位,不可能出现一条指令被分割在两个段中。

这一节在真题里被考过的形式

三道题,每一道都是"判断四条叙述"的形式,考的全是概念边界,没有计算。 可以说这一节的全部考法就是"把虚存的几条性质说反,看你认不认得出来"。

  • 判断虚拟存储器的四条叙述(2012-25)。答虚拟存储只能基于非连续分配技术。⚠️ 另三条把两个上限都说错了:"只受外存容量限制"和"只受内存容量限制"都是错的,正确的是两者之和与地址位数取小(速记第七条);"只能基于连续分配"更是反的。
  • 判断进程虚拟地址空间的四条叙述,选错误的(2023-28)。错项是"虚拟地址的大小由主存和硬盘的大小决定"——虚拟地址位数由体系结构定死,跟你装多少内存硬盘无关。另三条都对:每个进程有独立的虚拟地址空间、malloc() 返回的是虚拟地址、数据段和代码段可以有不同访问权限。
  • 判断虚存系统下指令执行过程的四条叙述(2026-24)。答异常由操作系统处理。⚠️ 另三条正好把分工全说反了:地址转换由 MMU 硬件做(不是 OS)、页表项由 OS 填(不是编译器)、缺页由硬件触发但由 OS 处理(不是硬件直接处理)。这道题就是速记第八条的完整考法。

复习优先级必须拿满,且只需记边界不需要算。 速记第七条(两个上限)和第八条(软硬件分工) 是三道题里两道的直接答案。第五条(缺页为什么必须是 Fault)会在 请求页式管理再用一次。局部性原理那个反例读一遍建立直觉即可, 不会单独设问。

易错:认为虚拟内存的容量等于外存大小、或等于内存加外存。上限是两者之和与地址位数决定的寻址空间取小

易错:认为虚拟地址的大小由装了多少内存和硬盘决定。它由体系结构的地址位数定死。

易错:认为地址转换由操作系统完成。是硬件 MMU——每条访存指令都要做一次,软件做不起。

易错:认为页表项的内容由编译器确定。页框归谁是运行时的全局状态,只能由 OS 填写。

易错:认为缺页中断由硬件直接处理。硬件只负责触发,找页框、选淘汰页、读盘、改页表都是 OS 做的。

易错:认为虚拟内存可以建立在连续分配之上。必须是离散分配,否则"部分装入"根本不成立。

易错:把缺页中断当成普通的外中断。它是故障(Fault)——在指令执行中途触发,处理完要重新执行该指令

易错:认为虚拟内存没有代价、是纯粹的改进。它永远比全部装入慢,换来的是能跑更大的程序、更多的进程。

教材出处
  • 请求分段所需的三项硬件支持(请求段表机制、缺段中断机构、地址变换机构)、段表项的六个新增字段(存取方式、访问字段 A、修改位 M、存在位 P、增补位、外存始址)、以及"分段是信息的逻辑单位,不可能出现一条指令被分割在两个分段中":汤小丹《计算机操作系统》5.5.1 节「请求分段中的硬件支持」,p173

相关知识

段页式管理请求页式管理运行时内存映像与地址空间中断和异常的处理

真题练习