超标量处理器设计 Chapter 3 笔记

超标量处理器设计 Chapter 3 笔记

Tue Mar 31 2026
2987 words · 11 minutes

Chapter 3 虚拟存储器学习笔记

1. 本章主线

第三章讨论的是:为什么处理器要引入虚拟存储器,以及虚拟地址如何一步步被转换成物理地址
从体系结构角度看,这一章的核心不是“存储器容量变大”这么简单,而是下面三件事:

  1. 地址重定位:程序看到的是虚拟地址,真正访问的是物理地址。
  2. 保护与共享:不同进程彼此隔离,但又能按需共享代码和数据。
  3. 性能补救:页表查询太慢,所以需要 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

它不是简单的“查不到”,而是一个需要操作系统参与处理的异常:

  1. 硬件发现页不在内存中。
  2. 触发异常,跳到操作系统的异常处理程序。
  3. 操作系统在磁盘中找到对应页面。
  4. 找一个空闲页框,或者选一个 victim page 换出。
  5. 若被换出的页是脏页,需要先回写。
  6. 把需要的页装入物理内存。
  7. 更新页表项。
  8. 重新执行导致异常的那条指令。

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. 本章最值得记住的几个结论

  1. 虚拟存储器的核心是映射、保护和共享,不只是“扩容”。
  2. 地址翻译本质上是 VPN -> PFN,offset 不变。
  3. 单级页表简单但大,多级页表省空间但更慢。
  4. Page Fault 是操作系统参与的异常,不是普通缓存未命中。
  5. TLB 是页表项的 Cache,用来弥补地址翻译太慢的问题。
  6. ASID 用来减少进程切换时清空 TLB 的代价。
  7. TLB 和 Cache 结合后,真正难点变成“快”和“正确”之间的折中。
  8. VIPT 是工程上很重要的折中:早索引、晚校验。

10. 复习时可以自问的题

  1. 为什么说虚拟存储器首先是地址管理机制,而不是容量扩展机制?
  2. 为什么多级页表会逼着处理器必须使用 TLB?
  3. TLB miss 和 Page Fault 的区别是什么?
  4. 页表项里的 valid、dirty、use 位各自解决什么问题?
  5. 为什么进程切换会影响 TLB?ASID 是怎么缓解的?
  6. 为什么“页内偏移不变”对 VIPT Cache 很重要?
  7. 两个虚拟地址映射到同一个物理地址时,为什么会引发 aliasing?

11. 一句话总结

第三章的核心可以浓缩成一句话:
虚拟存储器把“程序看到的地址世界”和“机器真实的存储世界”分开了,而 TLB、Cache 和流水线设计的任务,就是把这种抽象做得既安全又足够快。


Thanks for reading!

超标量处理器设计 Chapter 3 笔记

Tue Mar 31 2026
2987 words · 11 minutes

评论