Chapter 3 虚拟存储器学习笔记
1. 本章主线
第三章讨论的是:为什么处理器要引入虚拟存储器,以及虚拟地址如何一步步被转换成物理地址。
从体系结构角度看,这一章的核心不是“存储器容量变大”这么简单,而是下面三件事:
- 地址重定位:程序看到的是虚拟地址,真正访问的是物理地址。
- 保护与共享:不同进程彼此隔离,但又能按需共享代码和数据。
- 性能补救:页表查询太慢,所以需要 TLB;加入 Cache 后还会出现新的设计冲突。
可以把本章理解成一条链:
VA -> Page Table -> PA -> TLB/Cache -> Pipeline
2. 为什么需要虚拟存储器
2.1 解决的问题
- 程序使用的地址空间可以比物理内存大。
- 程序不需要关心自己最终被放在物理内存哪里。
- 多个进程都可以从“地址 0”开始运行,彼此互不干扰。
- 操作系统可以把不常用页面换到磁盘,需要时再调回。
- 可以对不同页面设置不同权限,实现读/写/执行保护。
2.2 重要理解
- 虚拟存储器首先是一个地址管理机制,其次才是“看起来内存变大”。
- 对程序来说看到的是连续的虚拟空间。
- 对硬件来说真正访问的是离散的物理页框(frame)。
- 操作系统负责维护“虚拟页 -> 物理页框”的映射关系。
3. 地址转换
3.1 基本单位
- Page:虚拟存储器按页管理,典型页大小是 4KB。
- Frame:物理内存中与 page 等大小的存储块。
- VPN:虚拟页号。
- PFN:物理页框号。
- Offset:页内偏移,地址转换前后保持不变。
3.2 地址转换的本质
虚拟地址可以拆成:
[ VPN | Offset ]物理地址可以拆成:
[ PFN | Offset ]因此地址转换本质上就是:
- 用
VPN查页表,得到PFN - 保留原来的
Offset - 最后拼成物理地址
PA
3.3 单级页表
单级页表的思路最直接:
- 每个虚拟页在页表中对应一个 PTE
- PTE 里记录该页是否有效,以及它映射到哪个物理页框
- 处理器先找到页表基址,再用 VPN 索引页表项
优点:
- 结构简单,概念直观。
缺点:
- 页表可能非常大。
- 对 32 位地址空间来说,4KB 页、4B PTE 时,页表大小约为 4MB。
- 如果系统里进程很多,每个进程都配一个完整线性页表,物理内存开销很大。
3.4 多级页表
多级页表的目标是:只为实际用到的地址空间分配页表。
基本做法:
- 把一个大页表拆成多级小页表。
- 一级页表先指向二级页表。
- 只有某段虚拟空间真的被使用时,才分配对应的下一级页表。
优点:
- 显著减小页表占用。
- 更符合“程序通常只使用部分虚拟地址空间”的现实。
代价:
- 页表遍历更复杂。
- 一次地址转换可能需要访问多次内存。
- 这也是后面必须引入 TLB 的直接原因。
4. Page Fault
4.1 什么时候发生
当处理器访问某个虚拟地址时:
- 如果页表项有效,说明该页已在物理内存中,可以继续访问。
- 如果页表项无效,说明当前页不在物理内存中,就会触发 Page Fault。
4.2 Page Fault 不是普通 miss
它不是简单的“查不到”,而是一个需要操作系统参与处理的异常:
- 硬件发现页不在内存中。
- 触发异常,跳到操作系统的异常处理程序。
- 操作系统在磁盘中找到对应页面。
- 找一个空闲页框,或者选一个 victim page 换出。
- 若被换出的页是脏页,需要先回写。
- 把需要的页装入物理内存。
- 更新页表项。
- 重新执行导致异常的那条指令。
4.3 成本为什么高
- 需要陷入内核,软件参与。
- 可能发生磁盘访问,延迟远大于普通 Cache miss。
- 若没有空闲页框,还要执行页面置换。
4.4 与处理器/Cache 的配合
书里强调了几个系统支持点:
- Page Fault 发生时,必须能精确回到出错指令重新执行。
store会影响页表中的脏位。load/store会影响页表中的使用位。- 若 D-Cache 采用 write back,页面换出前要先处理脏数据一致性问题。
4.5 PTE 中常见字段
Valid:页是否在物理内存中Dirty:页内容是否被修改过Use/Access:最近是否被访问过PFN:映射的物理页框号
5. 程序保护
5.1 为什么保护是虚拟存储器的重要目标
仅仅完成地址转换还不够,还必须保证:
- 用户进程不能随意改写操作系统空间
- 一个进程不能随意访问另一个进程的私有页面
- 共享页面可以共享,但访问权限必须受控
5.2 基本做法
- 在页表项中加入权限控制位。
- 常见权限包括:读、写、执行、用户态/内核态访问控制。
- 不同体系结构实现方式不同,但思想一致:权限跟着页走。
5.3 共享与隔离并不矛盾
- 不同进程可以把各自的虚拟地址映射到同一个物理页,实现共享。
- 也可以把相同虚拟地址映射到不同物理页,实现隔离。
- 这正是虚拟存储器最有价值的地方之一。
6. 加入 TLB
6.1 为什么必须有 TLB
如果每次访问内存都先查页表,再取数据,那么一次普通访存会变成:
- 先访问内存查 PTE
- 再访问内存取真正数据
多级页表时更糟,地址翻译本身就可能需要多次内存访问。
因此需要一个更快的小型缓存来缓存最近使用过的页表项,这就是 TLB。
6.2 TLB 的本质
- TLB 是 页表项的 Cache
- 缓存的是最近使用过的地址转换结果
- 输入通常是
VPN,输出是PFN及相关权限/状态位
6.3 TLB 命中与缺失
TLB hit:
- 直接得到 PFN
- 不必访问页表
TLB miss:
- 需要去页表中查找对应 PTE
- 查到后再把结果回填到 TLB
- 如果页表中该项本身无效,就不是普通 TLB miss,而会进一步触发 Page Fault
6.4 TLB 的组织
书里强调 TLB 常用全相联或高相联设计,原因是:
- TLB 容量通常较小
- 更希望降低 miss rate
- 允许用并行比较的方式快速匹配多个表项
常见做法:
- 分 I-TLB 和 D-TLB
- 采用多级 TLB
- 支持大页,以扩大 TLB 覆盖范围(reach)
6.5 大页的意义
页越大:
- 一个 TLB 项能覆盖的地址范围越大
- TLB miss 率可能下降
但代价是:
- 内部碎片可能增加
- 页面管理更粗糙
所以系统常同时支持多种页大小,让操作系统按场景选择。
6.6 ASID 的作用
如果进程切换时 TLB 中仍保留旧进程的项,就会混淆。
最直接的办法是切换进程时清空 TLB,但代价很高。
改进办法是引入 ASID(Address Space Identifier):
- 给不同进程的地址空间编号
- TLB 比较时同时比较
VPN + ASID - 进程切换时不必把整个 TLB 都清空
这样可以减少上下文切换的性能损失。
7. 加入 Cache 后的问题
7.1 关键矛盾
TLB 负责把 VA 变成 PA,Cache 又希望尽早开始访问。
于是出现一个经典问题:
- Cache 应该按虚拟地址访问,还是按物理地址访问?
7.2 几种常见方案
1. Physically-indexed, physically-tagged
特点:
- 完全使用物理地址访问 Cache
- 设计最简单,别名问题最少
缺点:
- 必须先完成 TLB 翻译才能访问 Cache
- 可能拉长流水线或增加访存延迟
2. Virtually-indexed, physically-tagged
特点:
- 用虚拟地址中的一部分尽早索引 Cache
- 同时并行访问 TLB
- 等 TLB 给出 PFN 后,再用物理 tag 做命中比较
优点:
- 能把 TLB 查询和 Cache 访问部分并行起来
- 是现实处理器中非常常见的折中方案
核心条件:
- Cache 的索引位最好落在页内偏移中
- 因为页内偏移在地址翻译前后不变
7.3 别名问题(aliasing / synonyms)
如果两个不同虚拟地址映射到同一个物理地址,而 Cache 又按虚拟地址组织,就可能出现:
- 同一份物理数据在 Cache 中有两份副本
- 一处修改后另一处副本没更新
- 后续读取得到旧值
这是虚拟 Cache 最棘手的问题之一。
7.4 如何缓解
书中提到的思路包括:
- 限制 L1 Cache 大小和组织方式,让索引只使用页内偏移位
- 用物理 tag 进行最终判定
- 借助更低一级、完全物理寻址的 L2 Cache 处理一致性
- 配合操作系统进行页着色(可理解为控制映射冲突的一种手段)
8. 将 TLB 和 Cache 放入流水线
8.1 目标
目标不是“功能能用”就够,而是:
- 不明显拉长关键路径
- 尽量把 TLB 和 Cache 的访问并行化
- 控制别名和一致性问题
8.2 重要结论
方案一:先 TLB,后 Cache
- 简单稳妥
- 但增加访存路径延迟
- 对高频流水线不友好
方案二:TLB 与 Cache 并行
- 前提是 Cache index 主要来自页内 offset
- TLB 提供 PFN 后做物理 tag 比较
- 这是性能和复杂度之间更现实的折中
8.3 容量限制的直觉
如果 L1 Cache 太大、set 太多,索引位就会超出页内 offset 的范围。
一旦超出,就不能仅凭 VA 的 offset 部分安全地并行索引 Cache,别名问题会明显变复杂。
所以:
- VIPT L1 Cache 的容量/相联度往往受到页大小约束
- 这就是很多处理器在 L1 上做出保守设计的原因
9. 本章最值得记住的几个结论
- 虚拟存储器的核心是映射、保护和共享,不只是“扩容”。
- 地址翻译本质上是 VPN -> PFN,offset 不变。
- 单级页表简单但大,多级页表省空间但更慢。
- Page Fault 是操作系统参与的异常,不是普通缓存未命中。
- TLB 是页表项的 Cache,用来弥补地址翻译太慢的问题。
- ASID 用来减少进程切换时清空 TLB 的代价。
- TLB 和 Cache 结合后,真正难点变成“快”和“正确”之间的折中。
- VIPT 是工程上很重要的折中:早索引、晚校验。
10. 复习时可以自问的题
- 为什么说虚拟存储器首先是地址管理机制,而不是容量扩展机制?
- 为什么多级页表会逼着处理器必须使用 TLB?
- TLB miss 和 Page Fault 的区别是什么?
- 页表项里的 valid、dirty、use 位各自解决什么问题?
- 为什么进程切换会影响 TLB?ASID 是怎么缓解的?
- 为什么“页内偏移不变”对 VIPT Cache 很重要?
- 两个虚拟地址映射到同一个物理地址时,为什么会引发 aliasing?
11. 一句话总结
第三章的核心可以浓缩成一句话:
虚拟存储器把“程序看到的地址世界”和“机器真实的存储世界”分开了,而 TLB、Cache 和流水线设计的任务,就是把这种抽象做得既安全又足够快。
