3. 内存虚拟化概述

在 KVM 内存虚拟化中,最容易混淆的不是“有没有页表”,而是“到底有几层地址、每层地址由谁解释”。Guest 操作系统看到的是一套连续、从 0 开始的内存视图,但它实际占用的物理内存却由宿主机分配和管理。要理解 Stage-2 映射,必须先厘清三个核心地址概念:Guest 虚拟地址(GVA)、Guest 物理地址(GPA)和Host 物理地址(HPA)。它们分别对应 Guest 进程视角、Guest 内核视角和宿主机硬件视角。

一、三个地址空间:谁看见什么

Guest 虚拟地址是 Guest 内部进程使用的地址,由 Guest 自己的页表完成 GVA→GPA 的转换。Guest 物理地址是 Guest 内核眼中的“物理内存”,它并不等于真实内存条上的地址,而是 KVM 为虚拟机抽象出的一段地址空间。Host 物理地址才是真正落到宿主机 DRAM 上的地址。KVM 通过 Stage-2 页表把 GPA 映射到 HPA,从而让 Guest 的“物理内存”落在宿主机的真实内存页上。

可以用一个典型场景理解:Guest 中某进程访问 0x7f2a_1000(GVA),Guest 页表把它翻译成 GPA 0x1a000;KVM 的 Stage-2 页表再把 GPA 0x1a000 翻译成 HPA 0x3f8c_0000。最终 CPU 访问的是宿主机物理地址 0x3f8c_0000。整个过程对 Guest 完全透明,Guest 始终认为自己独占物理内存。

KVM 内存虚拟化:三层地址空间与转换层次 Guest 虚拟地址 GVA Guest 进程视角 由 Guest 页表翻译 例:0x7f2a_1000 Guest 物理地址 GPA Guest 内核视角 KVM 抽象出的“物理内存” 例:0x1a000 Host 物理地址 HPA 宿主机硬件视角 真实 DRAM 地址 例:0x3f8c_0000 Guest 页表 Stage-2 页表 地址转换层次:从 GVA 到 HPA 的完整路径 第 1 层 GVA → GPA 第 2 层 GPA → HPA 最终访问 HPA → DRAM 关键点:Guest 页表由 Guest 内核维护,Stage-2 页表由 KVM 维护; Guest 看不到 HPA,KVM 通过 EPT/NPT 硬件加速完成 GPA→HPA 转换。

二、地址转换层次:两次翻译,一次访问

在传统操作系统中,一次内存访问只需一次页表翻译:虚拟地址→物理地址。而在 KVM 虚拟化环境中,Guest 的一次内存访问需要经过两次翻译:第一次由 Guest 页表完成 GVA→GPA,第二次由 KVM 的 Stage-2 页表完成 GPA→HPA。硬件通过 EPT(Intel)或 NPT(AMD)在 CPU 内部完成这两级翻译,避免每次访问都触发 VM-Exit。

如果没有硬件辅助,KVM 只能使用影子页表(Shadow Page Table):把 Guest 页表和 Stage-2 映射合并成一张直接 GVA→HPA 的页表,由 KVM 维护。这种方式实现复杂、同步开销大,且 Guest 页表更新时需要大量拦截。现代 KVM 默认使用 EPT/NPT,Guest 页表更新不再需要 VM-Exit,只有 Stage-2 页表缺页时才由 KVM 处理。

核心结论:GVA→GPA 由 Guest 负责,GPA→HPA 由 KVM 负责。Stage-2 映射是 KVM 内存虚拟化的核心机制,它决定了 Guest 物理内存最终落在宿主机的哪些物理页上。

三、内存虚拟化的目标与约束

内存虚拟化的目标可以归纳为四点:隔离性、透明性、高效性和可管理性。隔离性要求不同虚拟机的内存互不可见;透明性要求 Guest 无需修改即可运行;高效性要求地址转换不能带来过大性能损失;可管理性要求宿主机能够灵活分配、回收、迁移和超分内存。

目标 含义 实现手段 典型挑战
隔离性 各 VM 内存互不干扰 Stage-2 页表独立映射 侧信道攻击
透明性 Guest 无需感知虚拟化 GPA 抽象 + 硬件两级翻译 特殊指令拦截
高效性 接近原生的访问性能 EPT/NPT、TLB 合并 TLB 压力增大
可管理性 支持超分、迁移、回收 KVM 内存槽、脏页跟踪 内存超分导致换页

四、案例:一次 Guest 内存访问的完整路径

假设 Guest 中某进程读取变量 buf[0],其 GVA 为 0x7f2a_1000。CPU 首先查 Guest 页表,得到 GPA 0x1a000;随后查 Stage-2 页表,得到 HPA 0x3f8c_0000;最后从宿主机物理地址 0x3f8c_0000 读取数据。若 Stage-2 页表项不存在,CPU 触发 EPT Violation,KVM 捕获后分配宿主机物理页、建立映射,再重新执行指令。

GVA 0x7f2a_1000
  │  Guest 页表
  ▼
GPA 0x1a000
  │  Stage-2 页表(EPT/NPT)
  ▼
HPA 0x3f8c_0000
  │  硬件访问
  ▼
DRAM 数据

五、常见误区与复盘

  • 误区一:GPA 就是 HPA。GPA 只是 Guest 眼中的物理地址,KVM 可以把它映射到任意 HPA,二者没有固定对应关系。
  • 误区二:Guest 页表能直接控制 HPA。Guest 页表只能产生 GPA,最终 HPA 由 KVM 的 Stage-2 页表决定,Guest 无法绕过。
  • 误区三:有了 EPT 就不需要影子页表。EPT 解决了 GPA→HPA 的硬件加速,但 Guest 页表仍由 Guest 自己维护,两者职责不同。
  • 误区四:内存虚拟化只影响性能。它还直接影响隔离性、迁移能力和内存超分策略,是虚拟化安全与弹性的基础。

学习建议:理解本节时,始终问自己三个问题——这个地址是谁产生的?由谁翻译?最终落在哪里?把 GVA、GPA、HPA 三者的“产生者、翻译者、消费者”列清楚,Stage-2 映射的脉络就会非常清晰。

总结来说,KVM 内存虚拟化通过引入 GPA 这一中间地址层,把 Guest 的物理内存视图与宿主机真实内存解耦。Guest 负责 GVA→GPA,KVM 负责 GPA→HPA,硬件通过 EPT/NPT 加速两级翻译。理解这三层地址和两次翻译,是后续掌握 Stage-2 页表结构、缺页处理和内存超分机制的前提。