1. 课程导入

在云计算与服务器虚拟化场景中,内存是最容易被低估、却最直接影响虚拟机性能与密度的资源。CPU 虚拟化解决的是“指令在哪里执行”,而内存虚拟化解决的是“指令访问的地址最终落到哪一块物理内存”。KVM 作为 Linux 内核原生的虚拟化模块,其内存虚拟化能力建立在 x86 硬件辅助虚拟化之上,核心机制就是 Stage-2 映射(在 Intel 平台称为 EPT,在 AMD 平台称为 NPT)。理解 Stage-2,是理解 KVM 虚拟机内存性能、内存超分、热迁移、嵌套虚拟化等一系列高级主题的起点。

一、学习目标:从“会用”到“看得懂、调得动”

本课程不是简单地罗列 KVM 命令,而是希望建立一条从硬件到内核、从内核到 QEMU、从 QEMU 到虚拟机内部的可追踪链路。完成学习后,你应当能够达到以下目标:

  • 能说清 GVA、GPA、HVA、HPA 四类地址之间的转换关系,以及每一级由谁负责翻译。
  • 能解释 Stage-1(客户机页表)与 Stage-2(EPT/NPT)的分工,理解为什么需要两级翻译。
  • 能读懂 /sys/kernel/debug/kvm/ 下的关键统计,判断 EPT 缺页、大页使用与 MMU 缓存命中情况。
  • 能结合场景选择内存分配方式:匿名内存、HugePage、THP、NUMA 绑定与内存超分策略。
  • 能定位常见内存虚拟化问题:性能抖动、内存泄漏、迁移失败、嵌套虚拟化异常。
一句话概括本课程主线:地址翻译是骨架,Stage-2 是核心,性能与可管理性是最终落点。

二、Stage-2 映射核心概念:两级地址翻译

在没有虚拟化时,进程访问内存只需一次页表翻译:虚拟地址 VA → 物理地址 PA。引入虚拟机后,情况变成两级:虚拟机内部看到的“物理地址”其实并不是真正的物理地址,而是客户机物理地址 GPA。CPU 在执行客户机代码时,需要先把客户机虚拟地址 GVA 翻译成 GPA(Stage-1,由客户机页表完成),再把 GPA 翻译成宿主机物理地址 HPA(Stage-2,由 EPT/NPT 完成)。

硬件辅助虚拟化的价值在于:这两级翻译由 MMU 在一次遍历中协同完成,不需要为每次访问陷入 VMM。Stage-2 页表由 KVM 维护,QEMU 通过 KVM_SET_USER_MEMORY_REGION 等 ioctl 把虚拟机的内存槽(memory slot)注册给 KVM,KVM 再据此建立 GPA → HVA → HPA 的映射。因此,Stage-2 既是隔离边界,也是性能关键路径。

KVM 两级地址翻译:Stage-1 与 Stage-2 协同 GVA 客户机虚拟地址 GPA 客户机物理地址 HPA 宿主机物理地址 Stage-1 客户机页表 Stage-2 EPT / NPT 客户机内核 维护 Stage-1 页表 CR3 指向客户机页表 KVM 模块 维护 Stage-2 页表 处理 EPT Violation QEMU 进程 注册 memory slot 分配 HVA 与后端内存 关键路径:GVA → GPA(Stage-1)→ HPA(Stage-2),MMU 一次遍历完成,避免每次访问陷入 VMM Stage-2 既是虚拟机内存隔离边界,也是内存性能与超分能力的核心支撑

三、整体知识框架:从地址翻译到性能调优

本课程的知识框架可以分成四层,层层递进:

层次 核心问题 关键对象 典型工具/接口
地址翻译层 GVA 如何最终落到 HPA Stage-1、Stage-2、TLB、MMU EPT/NPT、CR3、VPID
内存管理接口层 QEMU 如何把内存交给 KVM memory slot、HVA、RAMBlock KVM_SET_USER_MEMORY_REGION
性能优化层 如何降低翻译开销与缺页开销 大页、THP、HugePage、TLB 命中 KVM 统计、perf、tracepoint
高级场景层 超分、热迁移、嵌套虚拟化如何工作 脏页跟踪、影子页表、L2 EPT dirty log、migration、nested

可以看到,Stage-2 并不是孤立的一层,它向上承接 QEMU 的内存槽注册,向下依赖硬件 EPT/NPT 能力,同时又被大页、TLB、脏页跟踪等机制影响。学习时如果只记住“EPT 是硬件加速的页表”,很容易在遇到性能问题时无从下手。

四、典型场景:一次客户机内存访问发生了什么

假设虚拟机内一个应用程序读取变量 x,地址为 GVA。CPU 首先用客户机 CR3 找到 Stage-1 页表,把 GVA 翻译为 GPA;随后 MMU 使用 EPTP(EPT 指针)找到 Stage-2 页表,把 GPA 翻译为 HPA;最后访问 HPA 对应的物理内存。如果 Stage-2 页表中该 GPA 尚未建立映射,就会触发 EPT Violation,KVM 捕获后根据 memory slot 找到对应的 HVA,再建立 GPA → HPA 映射,并重新执行指令。

# 查看 KVM 全局统计,观察 EPT 缺页与 MMU 缓存情况
cat /sys/kernel/debug/kvm/* 2>/dev/null | head -n 40

# 典型关注字段
# ept_violation      : Stage-2 缺页次数
# mmu_cache_miss     : MMU 缓存未命中
# mmu_shadow_zapped  : 影子页表回收次数
# hugepages          : 大页使用情况
当 ept_violation 持续偏高时,通常意味着 Stage-2 映射建立不充分或内存访问模式导致频繁缺页。此时应优先检查是否启用大页、是否发生内存超分、以及客户机是否频繁申请/释放内存。

五、常见误区与复盘

  • 误区一:把 GPA 当成真实物理地址。 GPA 只是客户机视角的“物理地址”,真正落到 DRAM 的是 HPA,中间必须经过 Stage-2。
  • 误区二:认为 EPT 一定比影子页表快。 EPT 减少了 VM Exit,但两级页表遍历本身有开销;如果缺页频繁,性能仍可能下降。
  • 误区三:忽略 memory slot 的对齐与合并。 不合理的 slot 划分会增加 KVM 查找开销,甚至影响热迁移的脏页跟踪效率。
  • 误区四:只调 QEMU 参数,不看 KVM 统计。 性能问题往往需要结合 /sys/kernel/debug/kvm/ 与 perf kvm 一起判断。
学习本课程时,建议始终带着一个问题:当前这次内存访问,究竟在哪一级翻译上花了时间? 只有把地址翻译链路和性能观测手段对应起来,才能真正做到“看得懂、调得动”。

接下来的章节将沿着“地址翻译 → Stage-2 建立 → 性能优化 → 高级场景”的路径逐步展开。本章只需要建立一张全局地图:GVA、GPA、HVA、HPA 四类地址,Stage-1 与 Stage-2 两级翻译,以及 KVM、QEMU、硬件 MMU 三者的分工。把这张地图记牢,后续每一章都会在这张地图上增加新的细节。