《操作系统导论》第21章:超越物理内存:机制 - 深度知识架构
1. 核心矛盾 (The Crucial Problem)
物理内存容量极其有限,但多道程序的发展和程序员对易用性的需求,要求操作系统必须能够利用大而慢的设备(如磁盘),透明地为多个进程提供巨大虚拟地址空间的假象。
2. 核心概念 (Core Concepts)
- 交换空间 (Swap Space):
- 定义:在硬盘上开辟出来的一块特定空间,用于物理页的移入(换入,page in)和移出(换出,page out)。
- 角色:物理内存的“外部仓库”。它决定了系统在某一时刻能够使用的最大内存页数,是支撑巨大虚拟内存假象的物质基础。
- 存在位 (Present Bit):
- 定义:页表项 (Page Table Entry, PTE) 中的一个硬件控制位,用于指示该虚拟页当前是驻留在物理内存中(1),还是被踢到了磁盘上(0)。
- 角色:硬件用来判断是否需要触发异常的“报警开关”。
- 页错误 / 页未命中 (Page Fault / Page Miss):
- 定义:当程序尝试访问一个存在位为 0(即在磁盘上)的虚拟页时,硬件触发的一种特殊异常。
- 角色:操作系统的“求助信号”。它强制暂停当前进程,将控制权交还给操作系统以解决缺页问题。
- 页错误处理程序 (Page-Fault Handler):
- 定义:操作系统内部专门用来处理页错误异常的一段内核代码。
- 角色:内存的“搬运工”。它负责寻找空闲物理页帧、发起磁盘 I/O (Input/Output,输入/输出) 请求以读取数据,并在完成后更新 PTE 和 TLB。
- 交换守护进程 / 页守护进程 (Swap Daemon / Page Daemon):
- 定义:操作系统中负责在后台主动释放内存的线程。它受到高水位线 (High Watermark, HW) 和低水位线 (Low Watermark, LW) 的控制。
- 角色:内存的“后台清道夫”。确保系统始终保留有小部分空闲内存,而不必等到内存彻底耗尽才去被动清理。
3. 逻辑演进 (Logical Evolution)
为了打破物理内存大小的限制,操作系统经历了如下的推导过程:
- 最初的妥协方案(内存覆盖):在早期系统中,没有操作系统的支持,程序员必须使用“内存覆盖 (Memory Overlays)”技术,手动编写代码将需要的数据或代码块移入移出内存。
- 遇到的问题:这极大地加重了程序员的负担,毫无易用性可言。
- 演进方案 1(引入磁盘与交换空间):为了提供易用的大内存假象,操作系统接管了这项工作。系统在磁盘上预留“交换空间”,将当前不用的页存入磁盘。
- 遇到的问题(如何识别和处理):硬件在进行地址转换时,怎么知道这个页在内存里还是在磁盘里?如果在磁盘里,硬件自己去读盘吗?
- 演进方案 2(存在位与 OS 介入):硬件在 PTE 中增加了存在位。如果在内存中,硬件直接转换;如果不在内存中,硬件不自己处理(因为磁盘 I/O 细节太复杂),而是触发页错误异常,把脏活累活丢给操作系统 (Operating System, OS) 的页错误处理程序。
- 遇到的问题(内存满了怎么办):当 OS 想要把一个页从磁盘换入内存时,如果物理内存已经满了怎么办?
- 最终成熟方案(替换策略与后台守护进程):如果内存满了,OS 必须先挑选一页踢出(这需要页替换策略)。为了避免每次发生页错误时都要同步等待旧页换出,OS 引入了交换守护进程。当空闲页数量低于 LW 时,守护进程在后台默默踢出旧页,直到空闲页数量达到 HW,从而保证系统永远有备用的空闲内存。
4. 机制与策略 (Mechanisms vs. Policies)
本章只探讨了机制,将策略留给了下一章,体现了操作系统经典的模块化解耦:
- 底层的“实现手段”(机制 - Mechanisms):
- 异常触发机制:硬件检查存在位并抛出页错误。
- 上下文切换机制:在发起慢速的磁盘 I/O 去换入页面时,OS 会将当前进程置于阻塞 (Blocked) 状态,并切换到其他就绪进程执行(交叠,Overlap)。
- 聚集 (Clustering) 机制:交换守护进程可以将多个需要换出的页收集起来,一次性分组写入磁盘,以提高磁盘效率。
- 上层的“决策逻辑”(策略 - Policies):
- 页替换策略 (Page-Replacement Policy):当内存不足时,“究竟该挑哪一个页踢到磁盘上?”(下一章的核心议题)。
- 阈值策略:高水位线 (HW) 和低水位线 (LW) 设置为多少才最合适。
5. 设计折衷 (Design Trade-offs)
- 牺牲“部分情况下的性能”,换取“地址空间的无限假象与多道程序的繁荣”:磁盘的速度通常比内存慢一万到十万倍。如果程序经常发生页错误,它将以磁盘的速度运行,性能出现断崖式下跌(称为抖动,Thrashing)。操作系统容忍了这种极端的潜在性能开销,换取了所有进程都可以无所顾忌地申请巨大内存空间的自由。
- 牺牲“硬件的大包大揽”,换取“操作系统的灵活性”:在处理 TLB 未命中时,有些硬件还会帮 OS 遍历页表。但在处理页错误时,硬件立刻“甩锅”。因为磁盘操作极其缓慢且细节繁琐,花费数千条指令让 OS 介入处理的开销相较于慢速磁盘 I/O 完全可以忽略不计。这种折中极大地简化了硬件的复杂性。
6. 关键洞察 (Key Insights)
- 把工作放在后台 (Background Processing/Laziness):在计算机系统中,当必须执行高昂的代价操作(如磁盘写入)时,不要阻挡当前的工作流。将其转交给后台守护进程去惰性处理。这不仅隐藏了延迟,还赋予了系统“合并 (Clustering)”多次操作以提升效率的机会。
- 重叠 (Overlap) 是掩盖 I/O 延迟的终极武器:当处理页错误的进程因为等待磁盘而被阻塞时,CPU 绝不会闲着。OS 会立即调度其他进程执行。通过并发交叠计算与 I/O,系统在宏观上维持了极高的效率。
- 极其完美的透明性:当一个程序执行
mov读取数据时,它完全不知道底层爆发了一场战争——发生异常、陷入内核、进程休眠、硬盘寻道读取、更新页表、唤醒进程并重新执行指令。应用程序只觉得这条指令执行得“似乎稍慢了一点点”。这是操作系统抽象之美的高体现。
导师的下一步建议: 我们现在已经通过这套完美的机制,让物理内存成功突破了它的物理极限。但是我们留下了一个非常致命的悬念:当内存满了,必须把某个页面踢到磁盘上时,我们应该踢谁? 如果踢错了(比如踢出了一个马上就要被用到的页),程序就会陷入灾难性的龟速。