Appearance
虚拟存储器
2026 大纲 三(七)虚拟存储器(基本概念;页式——页表、地址转换、TLB;段式与段页式的基本原理)。页式要求掌握到能完成地址转换,段式与段页式知道结构与转换步骤即可。
程序写死的那个地址,其实一个字节都没落在主存上
写程序时你会用到形形色色的地址——数组首址、函数入口、栈顶。这些地址在编译时就定下来了,可是编译器怎么知道这个程序运行时会被装到主存的哪个位置?如果同时跑两个程序,它们的地址岂不是要撞车?更别说程序本身可能比整个主存还大。
虚拟存储器的解法是把这三个问题一起打包:程序一律使用虚拟地址,硬件在每次访存时把它翻译成物理地址。于是编译器不必关心装到哪里、进程之间自然隔离、程序也可以比主存大(用不到的部分留在磁盘上)。
这套机制里有两条贯穿全篇的规则,先立住:
🔴 翻译只换高位那一段。虚页号换成实页号,页内地址原样保留——位数不变、取值也不变。由此直接推出:实页号的位数由物理地址位数决定,与虚拟地址位数无关。
🔴 顺序是先翻译、后访存。 MMU 翻译期间可能触发 TLB 缺失、缺页、访问越权;拿到物理地址之后 Cache 才开始查找。所以"Cache 缺失"不属于 MMU 在地址转换过程中检测的事件。
⚠️ 还要提醒一句:虚拟地址位数与物理地址位数相互独立。32 位虚拟地址配 24 位物理地址完全正常(虚页号 20 位而实页号只有 12 位),也有物理地址更宽的系统。做题时两个位数分别读题给的条件,不要拿一个去推另一个。
地址合成是位串拼接:
交互可视化
一、与 Cache 的类比
主存相当于磁盘的 Cache——同一种"用小而快的存储器缓存大而慢的存储器"的思想,只是层次不同,对照着记效率最高:
| Cache ↔ 主存 | 主存 ↔ 磁盘(虚存) | |
|---|---|---|
| 传输单位 | 块(行) | 页 |
| 未命中叫什么 | 缺失(miss) | 缺页 |
| 未命中代价 | 几十~上百个时钟周期 | 约 |
| 由谁处理未命中 | 硬件自动完成 | 操作系统介入处理 |
| 写策略 | 写回或写直达 | 一律写回 |
| 映射方式 | 直接/组相联/全相联 | 相当于全相联 |
二、页式虚拟存储器
虚拟地址空间和物理地址空间都按固定大小切成页,都是"高位段
页表项

图 7.40 主存中的页表示例(袁春风《计算机组成与系统结构(第 3 版)》p256)
| 字段(别名) | 作用 |
|---|---|
| 存放位置(实页号 / 页框号) | 装入位为 1 时指向主存页框;为 0 时给出磁盘上的位置 |
| 装入位(有效位、存在位) | 该页是否已调入主存 |
| 修改位(脏位) | 该页是否被写过,换出时据此决定要不要写回磁盘 |
| 使用位(访问位、替换控制位) | 配合替换策略(如 LRU、FIFO) |
| 访问权限位(保护位) | 可读 / 可写 / 可执行 |
| 禁止缓存位 | 该页是否允许装入 Cache,用于保证数据一致性 |
地址转换与缺页处理

图 7.42 分页式虚存的地址转换(袁春风《计算机组成与系统结构(第 3 版)》p259)
转换四步:页表基址寄存器给出页表首址 → 以虚页号为下标定位到对应页表项 → 检查装入位,为 1 则取出实页号 → 实页号与页内地址拼接成物理地址。这一整套由 MMU(存储管理部件)完成,TLB 是它的一部分。
三项检查各归谁,是判断题反复取材的地方:装入位
装入位为 0 时,操作系统的缺页处理走五步:
- MMU 发现装入位为 0,触发缺页异常
- 缺页处理程序按存放位置字段从磁盘读出该页
- 找一个空闲页框存放;若无空闲页框,则选一页换出(根据修改位决定是否写回磁盘)
- 更新页表
- 返回发生缺页的那条指令,重新执行它
🔴 第 5 步返回的是发生缺页的那条指令本身,不是它的下一条。 这是缺页处理最常被问反的一点。原因在于缺页属于故障(fault)类异常——故障的返回点就是当前指令,因为这条指令还没有做完。
第 5 步还有一个可计数的后果:由于该指令要重新执行,同一次访存会把地址转换的过程走两遍——第一遍触发缺页、第二遍才成功。统计访存次数或 TLB 查询次数时,这一遍最容易被漏掉。
顺带把"缺页为什么用异常机制而 Cache 缺失不用"说清:Cache 缺失由硬件自动完成、对软件完全透明;缺页却必须由操作系统介入(访问磁盘、更新页表、可能要选择换出页面),因而必须以异常的形式陷入内核。对照 异常和中断处理——缺页是由当前指令的访存动作引发的同步事件,习惯上称"缺页中断",但按定义它是异常不是中断。
虚页的三种状态(想弄清"未缓存"与"未分配"差在哪时展开)
- 已缓存页:装入位 1,存放位置字段是主存页框号;
- 未缓存页:装入位 0,存放位置字段是磁盘地址——已经分配了空间,只是尚未调入;
- 未分配页:装入位 0,存放位置字段为空——这个虚页根本还没有被分配过。
后两者的装入位都是 0,但缺页处理的结果不同:未缓存页从磁盘调入即可,访问未分配页则是真正的非法访问。
页表为什么大到必须分级:一张页表的规模算给你看,以及两级页表的两跳与代价(想弄清"多级页表省的到底是什么"时展开)
页表的项数由虚拟地址空间大小决定。以 32 位虚拟地址、4 KB 页为例:
而这是每个进程都要有一张的,全部常驻主存显然不合适。
两级页表把虚页号再切成两段:
转换要走两跳:页目录基址寄存器 → 页目录项(给出二级页表的基址)→ 页表项(给出实页号)。
代价是访存次数增加:TLB 缺失时,两级页表要访问 2 次主存才能拿到实页号,再加 1 次取数据。收益是二级页表可以按需建立,没有用到的地址区间不必为它分配页表。64 位系统通常采用四级页表。
三、TLB(快表)
页表在主存中,这意味着一次访存要变成两次:先查页表、再取数据。解决办法与 Cache 完全相同——把最近用过的页表项缓存起来,这个缓存就是 TLB(Translation Lookaside Buffer,快表):TLB 之于页表,正如 Cache 之于主存。
位置也要记准:页表在主存里、每个进程一张,首址记在页表基址寄存器;TLB 在 CPU 内部、用 SRAM 实现、常采用全相联或路数较少的组相联。
采用组相联时字段切分与 Cache 同构,只是被切的对象是虚页号:
🔴 组号取自虚页号的低位,不是虚拟地址的低位。 虚拟地址的低位是页内地址,它根本不参与 TLB 查找。切错这一步,标记位数会连带全错。
举一组数走一遍:虚拟地址 30 位、页大小 4 KB、TLB 共 16 项 2 路组相联。页内地址
一个 TLB 项的构成是 标记
TLB 装满后同样需要替换,常用 LRU,走查手法与 Cache 的替换算法 完全一样——命中时也要更新最近使用记录,否则替换决策会出错。
四、TLB、页表与 Cache 的联合访问

图 7.43 TLB 和 cache 的访问过程(袁春风《计算机组成与系统结构(第 3 版)》p260)
注意页内地址那 12 位自始至终没有进入 TLB,它直接旁路到物理地址的低位。
三者各有命中与否,共 8 种组合,其中 3 种在逻辑上不可能出现。判断靠两条判据:
🔴 判据一:TLB 命中
该页必在主存。 TLB 项是页表项的副本,能命中说明它曾经有效,所以"TLB 命中而页表缺失"自相矛盾。这条成立的前提是换出某页时必须同步让对应的 TLB 项失效——反过来用就是:页表里 P 的页,同步之后其 TLB 项的有效位必须为 0。
🔴 判据二:页不在主存
Cache 不可能命中。 Cache 缓存的是主存的内容,主存里都没有的东西,Cache 里不可能有。
| TLB | 页表 | Cache | 可能 | 说明 |
|---|---|---|---|---|
| 命中 | 命中 | 命中 | ✓ | 最快路径 |
| 命中 | 命中 | 缺失 | ✓ | 需访问主存取数据 |
| 命中 | 缺失 | 命中 | ✗ | 违反判据一 |
| 命中 | 缺失 | 缺失 | ✗ | 违反判据一 |
| 缺失 | 命中 | 命中 | ✓ | 查页表得地址,数据恰在 Cache 中 |
| 缺失 | 命中 | 缺失 | ✓ | 查页表并访问主存 |
| 缺失 | 缺失 | 命中 | ✗ | 违反判据二 |
| 缺失 | 缺失 | 缺失 | ✓ | 缺页,最慢路径 |
虚实地址变换完整走一遍:从十六进制切字段到查表拼接与算术验算(想核对自己切字段与进制换算这两步时展开)
先把整条路径串一遍:查 TLB,命中即得实页号;缺失则查页表,装入位为 1 则得实页号并回填 TLB、为 0 则触发缺页异常交 OS 调页后重执行。拿到物理地址后才轮到 Cache——命中直接返回数据,缺失则访问主存并装入 Cache。
设定:某系统虚拟地址 32 位、物理地址 24 位、页大小 4 KB。页表部分内容如下(装入位为 1 才有效)。求虚拟地址 0008 2840H 对应的物理地址。
| 虚页号(十进制) | 装入位 | 实页号 |
|---|---|---|
| 128 | 1 | 0x00A |
| 129 | 0 | — |
| 130 | 1 | 0x018 |
| 131 | 1 | 0x0C3 |
第一步,定各字段位数。 页大小 4 KB
第二步,切出字段的值。 十六进制下每位对应 4 个二进制位,低 3 位十六进制正好是 12 位页内地址:
第三步,查页表。 页表的虚页号列按十进制编号,需先换算 0x018。
第四步,拼接。
算术验算:
五、段式与段页式
页式按固定大小等分地址空间,段式则按程序的逻辑结构划分成长度不等的段(代码段、数据段、栈段),虚拟地址因而是二维的:段号

图 7.45 分段式虚存的地址转换(袁春风《计算机组成与系统结构(第 3 版)》p263)
段页式先分段、段内再分页,虚拟地址切成三段(段号
| 页式 | 段式 | 段页式 | |
|---|---|---|---|
| 划分依据 | 等长,硬件决定 | 变长,程序逻辑决定 | 先分段,段内分页 |
| 虚拟地址切几段 | 2 | 2 | 3 |
| 表项关键字段 | 实页号 | 段首址 + 段长 + 装入位 | 段表存页表基址;页表存实页号 |
| 地址合成方式 | 拼接 | 相加,需越界检查 | 拼接 |
| 无 TLB 时访存次数 | 2 | 2 | 3 |
| 对程序员 | 透明 | 不透明 | 段不透明、页透明 |
| 碎片 | 内碎片 | 外碎片 | 内碎片 |
这张表里最值得抓的两条差别:
🔴 段式的实质增量是"段长"。 段是变长的,所以必须检查「段内地址
段长」;页式不需要这项检查——页内地址的位数天然把取值范围限死了。也正因为段首址不必对齐到 2 的幂,段式合成地址靠相加而不是拼接。
🔴 段页式不能只查一次表。 段表项里存的是该段页表的基址(不是段的基址),拿到之后还得再查一次页表才得到实页号。所以无 TLB 时需要 3 次主存访问:查段表、查页表、取数据。
段的共享与保护、外碎片的紧凑、页面置换算法属于操作系统的内容,本节不展开。
考点速记
- 主存之于磁盘正如 Cache 之于主存,但缺页由操作系统处理、Cache 缺失由硬件处理;虚存相当于全相联且一律写回,两者都源于缺页代价极高(约
个时钟周期)。 - 地址转换只换高位段,实页号位数由物理地址位数决定、TLB 标记位数由虚拟地址位数决定;顺序固定为先翻译后访存(Cache 命中与否不在 MMU 的检测范围内);缺页是同步异常中的故障,处理完重新执行原指令。
- TLB 是页表的 Cache,组相联时切的是虚页号;两条判据(TLB 命中
页必在主存、页不在主存 Cache 不可能命中)决定 8 种组合里有 3 种不可能。段式多出段长与越界检查、地址靠相加;段页式无 TLB 时访存 3 次。
这一节在真题里被考过的形式(下方「真题练习」逐题对应):
- 问 TLB / Cache / Page 的命中组合中哪一种不可能发生:用两条判据筛。"TLB 命中而页缺失"违反判据一,"页缺失而 Cache 命中"违反判据二。
- 给页表内容与一个虚拟地址,问虚实地址变换的结果:切字段(十六进制低几位正好是页内地址)→ 虚页号换算成页表的编号进制 → 查装入位 → 装入位为 1 则拼接、为 0 则答缺页。⚠️ 页表行号常按十进制编号而虚页号切出来是十六进制,换算这一步最容易出错。
- 给 TLB 项数与路数,问 TLB 标记字段至少多少位:虚页号
虚拟地址位数 页内偏移位数;组数 项数 路数;标记 虚页号位数 组数。全相联时标记就等于完整的虚页号。 - 问把虚拟地址加宽几位后 TLB 项增加几位:只增加在标记字段上,实页号由物理地址位数决定、一位不动。
- 给页表某项 P
,问哪个 TLB 表项不可能出现在某组里:先用"虚页号 组数 组号"反推每个选项的虚页号,再用判据一——页表里 P 的那一页,其 TLB 项的有效位必须为 0,出现 V 的即为不可能。 - 挑关于缺页处理的错误叙述:常设的错点是"缺页处理完成后回到发生缺页的下一条指令执行"——应当重新执行该条指令本身。
- 问哪个事件不是在 MMU 地址转换过程中检测的:答 Cache 缺失。越权、缺页、TLB 缺失都在转换过程中,Cache 是拿到物理地址之后才查的。
- 大题里给一串虚页号问哪个 TLB 表项被替换:手法同 Cache 的 LRU 走查,先按
组数分组,只推被访问次数超过路数的那一组,命中也要更新 LRU 顺序。
易错:缺页处理后回到"下一条指令"。故障类异常的返回点是当前指令。
易错:拿虚拟地址的低位当 TLB 组号。要取虚页号的低位。
易错:用虚拟地址位数去推实页号位数。实页号由物理地址位数决定,两者相互独立。
易错:把 Cache 缺失算进 MMU 的检测范围。它在地址翻译完成之后才发生。
易错:段页式答成查两次表。段表给的是页表基址,无 TLB 时要访存 3 次。
教材出处
- 袁春风《计算机组成与系统结构(第 3 版)》§7.6 虚拟存储器:页表项各字段与三种虚页状态、图 7.40(p256)、页表过大问题与多级页表(p257)、图 7.42 分页式地址转换(p259)、图 7.43 TLB 与 cache 的访问过程(p260)、图 7.45 分段式地址转换(p263)
相关知识
异常和中断处理|存储器的层次结构|Cache 的基本工作原理