第 23 章 VAX/VMS 虚拟内存系统

VAX/VMS 操作系统(由 Dave Cutler 领导开发)是软硬件协同设计的典范。它面临的主要挑战是通用性(Generality),即需要在从低端到高端的各种 VAX 机器上高效运行。VMS 展示了如何通过软件创新来掩盖硬件缺陷(如过小的页大小)。

内存管理硬件

VAX-11 采用分页与分段的混合策略

  • 虚拟地址:32 位,分为 512 字节的页(Page)。
  • 地址结构
    • 高 2 位:段标识符(Segment ID)。
    • 中间 21 位:虚拟页号(VPN)。
    • 低 9 位:页内偏移(Offset)。

真实地址空间

VMS 将 32 位地址空间划分为四个象限(每段 1GB):

名称用途增长方向
P0程序空间 (Program)用户代码、数据、堆向下增长
P1控制空间 (Control)用户栈向上增长
S0系统空间 (System)操作系统代码、数据-
S1未使用保留-

1. 减少页表内存占用

VAX 的页大小仅为 512 字节,导致线性页表过大。VMS 采取了两种策略:

  1. 分段页表:P0 和 P1 之间的未使用空间不需要页表项(P0 基址/界限寄存器 + P1 基址/界限寄存器)。
  2. 内核虚拟内存中的页表:用户页表(P0/P1)本身存储在内核虚拟内存(S0)中。这意味着页表本身可以被交换到磁盘,从而缓解物理内存压力。
地址转换的复杂性

由于页表在虚拟内存中,查找 P0/P1 地址时,硬件可能需要先查找系统页表(S0)的物理地址。TLB 在此过程中至关重要,用于加速转换。

2. 空指针检测与内核映射

  • 空指针保护:P0 的第 0 页被标记为不可访问,访问 NULL 指针会触发段错误(Segmentation Fault)。
  • 内核映射:内核地址空间(S0)被映射到每个进程的高端地址。
    • 优点上下文切换时只需切换 P0/P1 寄存器;用户态与内核态数据拷贝非常容易(内核可直接访问用户指针)。
    • 保护:通过页表保护位,阻止用户态程序读写内核空间。

页交换 (Page Replacement)

VAX 硬件没有引用位(Reference Bit),这给页面置换算法带来了挑战。

1. 分段 FIFO (Segmented FIFO)

VMS 引入了驻留集大小 (RSS) 的概念,即每个进程在内存中允许保留的最大页数。

  • 策略:每个进程维护一个私有的 FIFO 队列。
  • 驱逐:当进程超过 RSS 时,利用 FIFO 驱逐页面。

2. 二次机会列表 (Second-Chance Lists)

为了缓解 FIFO 的性能问题(避免踢出热点页),VMS 使用两个全局列表作为“缓冲区”:

  1. 全局干净页列表 (Global Clean List):存放被驱逐但未修改的页。
  2. 全局脏页列表 (Global Dirty List):存放被驱逐且已修改的页。
回收机制 (Reclaim)

当进程 P 需要访问一个已被驱逐到全局列表的页时,可以直接从内存中**回收(Reclaim)**该页,避免磁盘 I/O。这使得算法表现接近 LRU。

3. 页聚集 (Page Clustering)

为了解决小页面(512B)导致的 I/O 低效问题,VMS 将全局脏列表中的页面分组成大批次(Cluster),一次性写入磁盘。

其他虚拟内存技巧

1. 按需置零 (Demand Zeroing)

防止信息泄露(看到其他进程的数据)需要将新分配的页清零。

  • 惰性策略:分配时仅在页表中标记“按需置零”且不可访问。
  • 触发:当进程实际写入该页时,触发缺页异常,OS 此时才分配物理页、清零并映射。
  • 收益:如果申请了内存但从未使用,则完全避免了物理分配和清零开销。

2. 写时复制 (Copy-On-Write, COW)

用于高效实现 fork() 和共享库。

  • 机制:将页面映射为只读。
  • 触发:当任一进程尝试写入时,触发保护异常。OS 复制该页,并将新旧页面重新映射为可读写。
  • 收益:避免了大量不必要的内存复制。
// 模拟空指针访问导致的段错误
int *p = NULL; // 虚拟地址 0
*p = 10;       // 触发异常:VPN 0 标记为无效