1532 字
8 分钟
内存虚拟化
2025-11-10
无标签

内存虚拟化#

基本经历了影子页表—> 两阶段页表(硬件翻译)的过程

影子页表#

核心思想:Hypervisor 使用一个“假的”页表(影子页表)。

影子页表原理#

参考 https://zhuanlan.zhihu.com/p/529084234

v2-f337fd7635b9ca659a7ed4462378961a_1440w.jpg

 即guest os建立的GVA—>GPA页表在页表基地址寄存器中被hypervisor用shadow页表替换掉。Shadow页表的建立过程比较复杂,以下为其简单原理:
(1)hypervisor为每个vm中的每个进程都维护一张合并了GVA—>GPA和GPA—>HPA两级页表关系的shadow页表。当guest os执行页表切换操作时,hypervisor将截获该操作,并用这张合并后的页表替换掉guest os本身的页表。它就像影子一样覆盖掉了guest os的页表,因此其被称为影子页表

(2)guest os在自身页表中建立GVA到GPA的映射关系

(3)当guest os通过GVA访问内存时,若该GVA在shadow页表中已建立,则可正常通过MMU访问内存,否则会引起缺页异常

(4)由于hypervisor在页表替换时知道被替换guest os页表的基地址,因此在缺页异常处理流程中可通过遍历其对应的页表,查询GVA对应的GPA

(5)GPA是在虚拟机初始化内存条时设置,并与一段host中的用户态虚拟地址HVA绑定,因此hypervisor可以通过GPA获取其对应的HVA

(6)此时可通过host内存管理模块的HVA获取到其对应的HPA

(7)最后对以上流程进行合并,计算得到GVA—>HPA之间的关系,并将其填到影子页表中

性能分析#

对于 guest os 的有关页表的操作,主要对于 Guest OS 页的修改,消耗巨大,需要 VM-EXIT 退出到 kvm 中进行修改。

内存类型Guest OS 在其页表中的意图KVM 在影子页表中的设置目的
应用程序数据页 (Data)可读可写可读可写尊重 Guest,保证性能
应用程序代码页 (Code)只读可执行只读可执行尊重 Guest,保证性能
Guest OS 页表页可读可写只读故意降级,设置陷阱以拦截页表修改
写时复制 (CoW) 页只读只读尊重 Guest,并将相应的 Fault 反射回去

两阶段页表#

为了提升影子页表的的性能,硬件对于虚拟化进行了扩展

不同架构的两阶段页表技术对比#

核心技术点Intel (x86)AMD (x86)ARM64 (AArch64)RISC-V
整体虚拟化技术Intel VT-xAMD-V (SVM)ARM Virtualization Extensions’H’ Extension (Hypervisor)
内存虚拟化功能名称EPT (Extended Page Tables)NPT (Nested Page Tables) / RVIStage 2 TranslationTwo-Stage Address Translation
Hypervisor 运行模式VMX Root OperationHost ModeEL2 (Exception Level 2)HS-Mode (Hypervisor-extended)
Guest OS 运行模式VMX Non-Root OperationGuest ModeEL1 (Exception Level 1)VS-Mode (Virtual Supervisor)
Stage 1 页表基址寄存器
(Guest OS 使用)
CR3CR3TTBR0_EL1 / TTBR1_EL1vsatp (Guest 视角)
Stage 2 页表基址寄存器
(Hypervisor 设置)
EPTP (在 VMCS 中)nCR3 (在 VMCB 中)VTTBR_EL2hgatp
硬件功能启用方式在 VMCS 中设置 “Enable EPT” 位在 VMCB 中设置 “Enable NPT” 位HCR_EL2 中设置 VMhstatus 等寄存器中设置
TLB 效率优化 (标签)VPID (Virtual Processor ID)ASID (Address Space ID)VMID + ASIDVMID + ASID
I/O 虚拟化 (IOMMU)Intel VT-dAMD-ViSMMU (System MMU)RISC-V IOMMU (标准制定中)

翻译过程#

在 ARM64 下启用 Stage2 翻译。

假设虚拟机里的一个应用程序(在 EL0)要访问一个内存地址 (GVA):

  1. CPU 处于 EL0: 准备执行指令。
  2. 选择页表 (硬件自动): CPU 发现 GVA 是一个低地址(用户空间地址),于是它会自动选择使用 TTBR0_EL1 寄存器。
  3. 第一阶段翻译 (Stage 1: GVA -> GPA):
    • CPU 从 TTBR0_EL1 中获取 Guest OS 设置的页表基地址。
    • MMU 硬件开始遍历这套由 Guest OS 控制的页表,最终将 GVA 翻译成客户机物理地址 (GPA)。
  4. 第二阶段翻译 (Stage 2: GPA -> HPA):
    • MMU 硬件并不会停下,而是立刻拿着上一步得到的 GPA。
    • 它从 VTTBR_EL2 寄存器(这个寄存器由在 EL2 的 KVM 控制)中获取 Stage 2 页表的基地址。
    • MMU 硬件继续遍历这套由 KVM 控制的页表,最终将 GPA 翻译成真正的宿主机物理地址 (HPA)。
  5. 访问内存: CPU 最终带着 HPA 去访问物理内存。

一个表格总结:

运行实体所在异常级别使用的页表寄存器负责的翻译阶段
KVM HypervisorEL2VTTBR_EL2Stage 2 (GPA -> HPA)
Guest OS 内核EL1TTBR1_EL1Stage 1 (GVA -> GPA, 内核空间)
Guest 应用程序EL0TTBR0_EL1Stage 1 (GVA -> GPA, 用户空间)

stage2 配置#

这里有一个问题:Stage 2 (GPA -> HPA) 这一个翻译阶段,是否需要 VM-EXIT 切换到 EL2 进行翻译呢?

答案是:不需要。Stage 2 的翻译过程完全由硬件(MMU)在后台完成,CPU 核心本身不需要切换到 EL2。

这是硬件虚拟化优势的关键点。必须严格区分“配置规则”和“执行规则”。

  • 配置规则 (由软件完成): 必须在 EL2 完成。
  • 执行规则 (由硬件完成): CPU 在 EL1/EL0 运行时,硬件自动执行。
过程谁来做?CPU 运行在哪个级别?解释
配置 Stage 2 页表和 VTTBR_EL2KVM 软件EL2这是 Hypervisor 的本职工作,必须在最高权限下设置好内存映射规则。
虚拟机执行一条 LDR / STR 指令Guest 软件 (App/OS)EL0 / EL1这是虚拟机在正常运行自己的代码。
执行完整的 GVA -> HPA 地址翻译硬件 MMU(CPU 核心仍然在 EL0 / EL1)这就是硬件辅助的精髓! MMU 作为一个独立的硬件单元,在后台完成了两阶段的查表工作。CPU 核心并没有改变它的异常级别。
处理 Stage 2 的翻译失败 (Fault)KVM 软件CPU 从 EL1/EL0 陷入到 EL2这是“例外”情况。只有当硬件无法独立完成翻译时,才需要软件介入。

硬件 MMU 将原本由软件模拟的影子页表功能卸载至硬件,使得常规内存访问路径完全在 EL0/EL1 执行,仅在 Stage-2 异常时陷入 EL2,从而规避了影子页表的性能瓶颈。