# Virtual Memory

“虚拟内存” 是操作系统提供的核心抽象之一,它为每个进程提供了一种错觉,使其认为自己独占了主内存

# Physical and Virtual Addressing

  • 物理地址 (Physical Addressing):这是硬件实际用于访问主内存中存储单元的地址。

    image-20250914190436013

  • 虚拟地址 (Virtual Addressing):这是 CPU 为程序生成或程序使用的逻辑地址。每个程序都认为自己拥有一个连续且专用的内存空间,使用这些虚拟地址

    image-20250914190452080

  • 内存管理单元 (Memory Management Unit, MMU):CPU 芯片上的专用硬件,负责实时进行地址转换,它使用存储在主内存中、由操作系统管理的一个查找表

# 地址空间 (Address Spaces)

地址空间是一个有序的非负整数地址集合,如果地址是连续的,则称为线性地址空间。来源假定总是使用线性地址空间

# 虚拟内存作为缓存工具 (VM as a Tool for Caching)

虚拟内存系统通过将主内存作为磁盘的缓存来高效利用内存

虚拟内存系统通过将虚拟内存划分为固定大小的块,称为虚拟页 (Virtual Pages, VPs),每页大小为 P=2pP = 2^p 字节。同样,物理内存也被划分为大小相同的物理页 (Physical Pages, PPs),也称为页帧 (page frames)

在任何给定时间,虚拟页集合被划分为三个不相交的子集:

  • 未分配页 (Unallocated):尚未被虚拟内存系统分配或创建的页面,它们没有关联数据,因此不占用磁盘空间。
  • 已缓存页 (Cached):已分配且当前缓存于物理内存中的页面。
  • 未缓存页 (Uncached):已分配但当前未缓存于物理内存中的页面。 图 9.3 通过一个例子展示了 VM 系统如何将主内存用作缓存,其中一些虚拟页已缓存到物理内存中,一些未分配,另一些已分配但未缓存

image-20250914191120938

# DRAM Cache Organization

为了区分内存层次结构中的不同缓存,来源将 L1、L2 和 L3 缓存称为 SRAM 缓存,而将虚拟内存系统在主内存中缓存虚拟页的机制称为 DRAM 缓存

DRAM 缓存的位置对其组织方式有着巨大影响:

  • 高成本的缓存未命中:DRAM 比 SRAM 至少慢 10 倍,而磁盘比 DRAM 慢约 10 万倍。因此,DRAM 缓存的未命中成本(需要从磁盘加载数据)远高于 SRAM 缓存的未命中成本(通常从 DRAM 主内存加载)。
  • 巨大的虚拟页大小:由于巨大的未命中惩罚以及访问磁盘第一字节的高成本,虚拟页通常设计得很大,大小范围从 4 KB 到 2 MB
  • 全相联缓存 (Fully Associative):为了最小化未命中率,DRAM 缓存通常是全相联的,这意味着任何虚拟页都可以放置在任何物理页中。
  • 复杂的替换策略:由于替换错误虚拟页的惩罚很高,操作系统会为 DRAM 缓存使用比硬件 SRAM 缓存更复杂的替换算法。
  • 写回 (Write-Back) 策略:由于磁盘访问时间很长,DRAM 缓存总是使用写回策略,而不是直写 (write-through) 策略,以减少写操作导致的磁盘 I/O

# Page Tables

虚拟内存系统通过页表来确定虚拟页是否缓存于 DRAM 中,以及如果缓存,则具体位于哪个物理页。如果未命中,则确定虚拟页在磁盘上的位置,选择牺牲页,并从磁盘复制到 DRAM

  • 页表的组成:页表是一个页表条目 (Page Table Entries, PTEs) 数组,虚拟地址空间中的每个虚拟页都在页表中有一个固定偏移量的 PTE。
  • PTE 的结构:每个 PTE 包含一个有效位 (valid bit) 和一个 nn 位地址字段。
  • 有效位:指示虚拟页是否当前缓存于 DRAM 中。如果设置,地址字段指向 DRAM 中对应物理页的起始地址。
  • 空地址:如果有效位未设置,空地址表示虚拟页尚未分配。
  • 磁盘地址:如果有效位未设置但地址非空,则地址指向磁盘上虚拟页的起始位置。
  • MMU 和 OS 的协作内存管理单元 (MMU) 中的地址转换硬件每次转换虚拟地址时都会读取页表。操作系统负责维护页表内容,并在磁盘和 DRAM 之间传输页面。 图 9.4 展示了一个页表的基本组织结构,其中显示了已缓存、未分配和已分配但未缓存的虚拟页如何映射到物理页或磁盘上的位置

image-20250914191632162

# Page Hits

当 CPU 读取虚拟内存中的一个字,且该字所在的虚拟页已缓存于 DRAM 中时,即发生页命中

image-20250914191912535

# Page Faults

在虚拟内存术语中,DRAM 缓存未命中被称为页错误 (page fault)

image-20250914191939083

页错误触发:当 CPU 引用虚拟页中的一个字,而该页未缓存于 DRAM 时,地址转换硬件会读取该虚拟页的 PTE。如果有效位为零,MMU 会触发页错误异常,将控制权转移给内核中的页错误异常处理程序

# 处理过程

  1. 选择牺牲页:异常处理程序在物理内存中选择一个牺牲页。
  2. 写回牺牲页:如果牺牲页已被修改(“脏页”),内核会将其内容写回磁盘。
  3. 更新 PTE:内核修改牺牲页的 PTE,以反映它不再缓存于主内存的事实。
  4. 加载新页:内核将请求的虚拟页从磁盘复制到物理内存的牺牲页位置。
  5. 更新 PTE:内核更新新页的 PTE,将其有效位置为 1,并指向其新的物理页号。
  6. 重启指令:处理程序返回,重启导致页错误的指令。由于虚拟页现在已缓存,该指令将正常执行,导致页命中

# Allocating Pages

当操作系统分配新的虚拟内存页面时(例如,作为 malloc 调用导致的结果),虚拟内存系统会为其在磁盘上创建空间,并更新对应的 PTE 以指向磁盘上的这个新位置

# Locality to the Rescue Again

尽管页错误惩罚巨大,但虚拟内存之所以能够高效工作,主要归功于局部性原理

  • 工作集 (Working Set):虽然程序在整个运行期间引用的不同页面总数可能超过物理内存的总大小,但局部性原理保证,在任何给定时间,程序倾向于在一个较小的活跃页面集合上工作,这被称为工作集驻留集 (resident set)
  • 避免磁盘 I/O:在工作集首次加载到内存后,后续对工作集的引用将导致页命中,无需额外的磁盘流量。
  • 颠簸 (Thrashing):如果程序的局部性差,导致工作集大小超过物理内存大小,就会发生颠簸 (thrashing) 现象,即页面持续不断地被换入和换出。这会导致程序性能急剧下降,明智的程序员在程序运行缓慢时会考虑是否发生了颠簸