Chap.8 虚拟内存 (Virtual Memory)
8.1 硬件和控制结构 (Hardware and Control Structures)
虚拟内存的实现是硬件(如MMU)和操作系统软件共同配合的奇迹。
核心概念核对 (Concept Check)
- 虚拟内存 (Virtual Memory): 一种存储分配方案,允许将辅助存储器(如磁盘)作为主存来寻址。虚拟大小受限于计算机的地址方案和磁盘空间,而非实际的物理内存。
- 局部性原理 (Principle of Locality): 算法利用局部性原理,预测哪些驻留页面在不久的将来最不可能被访问,从而将它们换出。局部性分为空间局部性和时间局部性。
- 页表项控制位 (Page Table Entry Bits):
- 页框号 (Frame number): 标识主存中页面的顺序编号。
- 存在位 (Present/Valid bit): 指示该页当前是否在主存中。
- 修改位 (Modify/Dirty bit): 指示该页自装入主存后是否被修改过。如果没有修改过,置换时就不需要将其写回磁盘。
底层逻辑剖析 (The 'Why')
- 为什么要用转换后备缓冲区 (TLB)? 每次虚拟地址转换都需要访问内存中的页表,导致访存时间翻倍。TLB 是一个专用的高速缓存,保存了最近使用的页表项。其目的是在绝大多数情况下,避免去磁盘或主存中检索页表项,极大提升地址转换速度。
- 为什么要设计多级页表 (Multilevel Page Tables)? 对于巨大的虚拟地址空间(例如 32 位或 64 位),单级页表会占用极其庞大的连续物理内存。多级页表允许页表本身也被分页并存放在虚拟内存中,只将顶层目录(Root page table)常驻主存。
- 反置页表 (Inverted Page Table) 的意义是什么? 传统页表大小与虚拟地址空间成正比,而反置页表的大小与物理内存成正比。它利用哈希表映射,每个物理页框只对应一个页表项。页表项通常包含:页号、进程标识符、控制位和用于解决哈希冲突的链指针。
- 为什么要结合分页和分段 (Combined Paging and Segmentation)? 分页对程序员透明,消除了外部碎片;而分段对程序员可见,支持不断增长的数据结构、模块化、共享和保护。段页式结合方案中,进程拥有一个段表,而每个段拥有自己的页表。
8.2 操作系统软件 (Operating System Software)
操作系统需要制定一系列策略来管理虚拟内存,以最小化缺页中断(Page Fault)带来的巨大开销。
核心概念核对 (Concept Check)
操作系统的虚拟内存策略主要包含以下六个方面:
- 读取策略 (Fetch Policy): 决定何时将页面调入主存。
- 请求分页 (Demand Paging): 只有当访问到该页某个位置而发生缺页时,才将其调入主存。
- 预分页 (Prepaging): 除了引发缺页的页面,还会将预期可能用到的其他页面一并调入。
- 放置策略 (Placement Policy): 决定页面放在哪个物理框(主要在NUMA架构中重要)。
- 置换策略 (Replacement Policy): 决定淘汰哪个页面。常见算法包括 OPT, LRU, FIFO, Clock。
- 驻留集管理 (Resident Set Management): 决定分配给进程多少个页框,以及置换范围。
- 清除策略 (Cleaning Policy):
- 请求清除 (Demand Cleaning): 只有当页面被选中替换时才写回磁盘。
- 预清除 (Precleaning): 在页面框被需要之前,将修改过的页面成批写回磁盘。
- 负载控制 (Load Control): 决定系统多道程序度,防止抖动。
底层逻辑剖析 (The 'Why')
- 为什么 Clock 算法比 LRU 更实用? 真正的 LRU (最近最久未使用) 算法需要为每次内存访问记录时间戳或维护链表,硬件开销极大。Clock (时钟) 策略通过“使用位 (Use bit)”来近似 LRU:当发生置换时,指针扫描环形缓冲区,跳过使用位为 1 的页(将其置 0),替换掉遇到第一个使用位为 0 的页。
- 局部置换与全局置换 (Local vs. Global Replacement): 固定分配策略要求分配给进程的页框数固定,因此发生缺页时,只能替换该进程自己的页面(局部置换)。
常见易错点 (Common Pitfalls)
- Belady 异常 (Belady's Anomaly): 学生常认为分配给进程的物理页框越多,缺页率一定越低。但在 FIFO 置换算法 中,增加页框数有时反而会导致更多的缺页传输,这就是著名的 Belady 异常。
- 驻留集 (Resident Set) vs 工作集 (Working Set):
- 驻留集 指的是一个进程当前实际存在于主存中的页面集合。
- 工作集 指的是一个进程在最近一段时间内被引用过的页面集合。系统应当试图让驻留集包含工作集,以降低缺页率。
- 抖动 (Thrashing): 这不是硬件故障!抖动是指在虚拟内存机制中,处理器将大部分时间耗费在换入换出页面上,而不是执行指令上的系统过载现象。通常因为多道程序度过高、进程分配的页框不足以覆盖其局部性引发。
8.3 - 8.6 具体操作系统的内存管理
不同的操作系统在实现虚拟内存时有各自的特色结构。
1. UNIX 和 Solaris 内存管理
UNIX SVR4 采用了四个关键数据结构来管理分页:
- 页表项 (Page Table Entry): 包含页框号、年龄、修改位、访问位、存在位等。
- 磁盘块描述符 (Disk Block Descriptor): 记录页面在交换设备(Swap device)上的逻辑设备号和块号。
- 页框数据表项 (Page Frame Data Table Entry): 描述物理页框的状态和引用计数。
- 交换使用表项 (Swap-Use Table Entry): 记录交换设备页面的使用计数。
2. Linux 内存管理
- 四级页表结构: 为了保持平台独立性并支持大地址空间,Linux 将虚拟地址分为四个字段:页目录 (Page Directory) 索引、页中间目录 (Page Middle Directory) 索引、页表 (Page Table) 索引,以及偏移量。
- 页大小: 基础单位通常是 4KB,但也支持大页(Hugepages,如 2MB),后者可以显著减少 TLB 访问次数从而提高性能。
3. Windows 内存管理
- 32位默认虚拟地址映射: Windows 为正常的 32 位用户进程提供了 4GB 的虚拟地址空间,默认划分为四个区域:
0x00000000 - 0x0000FFFF(64KB): NULL 指针分配区,不可访问,用于捕获空指针错误。0x00010000 - 0x7FFEFFFF(~2GB): 用户可用地址空间。0x7FFF0000 - 0x7FFFFFFF(64KB): 保护页(Guard page),不可访问,用于检查指针越界。0x80000000 - 0xFFFFFFFF(2GB): 操作系统内核专属区域,用户态不可访问。
总结 (Summary)
虚拟内存不仅让程序员摆脱了主存容量的物理限制,更通过分页、分段硬件与复杂的页面调度算法(置换策略、驻留集管理等),在**性能(时间和缺页率)与空间(内存利用率)**之间实现了极其微妙的平衡。理解本章的核心在于牢记 “局部性原理 (Locality)” 以及如何用最少的开销(如 TLB、Clock 算法)去逼近完美的预测。