【笔记】虚拟化技术的分层模型
虚拟化不是一种单独的技术,而是把一台机器拆成多个可管理执行环境时,对 CPU、内存和 I/O 分别建立的抽象与隔离。现代虚拟机通常同时使用硬件辅助执行、两阶段地址翻译、虚拟设备、半虚拟化 I/O 和设备直通,不能用“全虚拟化”或“半虚拟化”一个标签概括整条链路。
1. 中心模型:虚拟的是一台机器,不只是 CPU
一个可运行 Guest OS 的 VM,至少需要三类能力:
flowchart TB
G[Guest OS]
CPU[虚拟 CPU<br/>vCPU]
MEM[Guest Physical Memory]
IO[虚拟设备]
HCPU[CPU 虚拟化<br/>VM Entry / VM Exit]
HMEM[内存虚拟化<br/>Shadow Paging 或 EPT/NPT]
HIO[I/O 虚拟化<br/>模拟 / VirtIO / 直通]
G --> CPU --> HCPU
G --> MEM --> HMEM
G --> IO --> HIO
- CPU 虚拟化让 Guest 的内核代码能受控地使用物理 CPU;
- 内存虚拟化让 Guest 看到连续、独占的“物理内存”,同时限制它只能访问分配给自己的 Host 内存;
- I/O 虚拟化为 Guest 提供网卡、磁盘、时钟、中断控制器等设备,并把请求接到真实资源。
只解释 VM Entry/Exit,仍然不能回答 Guest 页表如何落到 Host 内存、虚拟网卡如何收发包,也不能构成一台完整 VM。
2. Hypervisor、VMM 与虚拟化栈
2.1 两个术语没有绝对统一的分界
Hypervisor 常译为“虚拟机监控器”或“虚拟机管理程序”;VMM 是 Virtual Machine Monitor。很多资料把两者当同义词,也有资料用 Hypervisor 指特权执行与隔离层,用 VMM 指用户态机器模型和管理进程。
因此,与其争论某个组件“算不算 Hypervisor”,不如明确它承担什么职责:
| 职责 | 典型内容 |
|---|---|
| 受控执行 | 进入 Guest、处理退出、注入中断、维护 vCPU 状态 |
| 地址空间 | Guest 内存映射、权限、缺页、脏页和迁移 |
| 机器模型 | 主板、固件、PCI 总线、网卡、磁盘和中断控制器 |
| 生命周期 | 创建 VM、配置资源、暂停、恢复、快照和迁移 |
2.2 KVM 与 QEMU 是职责互补,不是简单上下层
KVM 向用户态暴露 /dev/kvm。用户态程序创建 VM 和 vCPU,通过 KVM_RUN 让某个 vCPU 运行。KVM 使用 Linux 内核与 CPU 虚拟化扩展处理受控执行和内存隔离。
QEMU 的 system emulation 提供一台机器的模型,包括 CPU、内存和设备。它可以:
- 使用 KVM、Xen、macOS HVF、Windows WHPX 等 accelerator;
- 不使用硬件虚拟化,改用 TCG 做指令翻译;
- 提供传统模拟设备、VirtIO 设备、设备直通和管理接口。
所以“QEMU 只是外设模拟器”太窄,“KVM 单独就是一整套 VM 产品”也不准确。常见的 QEMU/KVM 栈,是 QEMU 负责机器模型与用户态控制,KVM 负责 Linux 内核里的虚拟化执行能力,两者通过 KVM API 协作。
2.3 Type 1 / Type 2 只能描述部署形态
Type 1 通常指虚拟化层直接控制硬件,Type 2 通常指虚拟机软件运行在通用 Host OS 之上。这个分类适合建立直觉,但面对 KVM、Hyper-V、macOS Virtualization/Hypervisor Framework 等现代组合时会变得模糊。
桌面产品有 Host OS 和 GUI,不代表所有虚拟化工作都由普通用户态代码完成;云上系统直接控制硬件,也不代表不存在完整的管理 OS 和用户态设备模型。分析真实系统时,应继续拆 CPU、内存、设备和管理面,而不是停在 Type 1/Type 2 标签上。
3. CPU 虚拟化:让 Guest 内核直接但受控地运行
3.1 为什么早期 x86 虚拟化困难
普通应用受 OS 控制,因为敏感操作只能在高特权级执行。Guest OS 自己也要以最高特权运行和管理页表、中断、设备;如果让它直接控制物理机器,就会破坏 Host 和其他 VM。
早期实现主要有两条路:
- 解释或动态二进制翻译:VMM 分析 Guest 指令,把不能安全直接执行的部分改写或模拟;
- 半虚拟化:修改 Guest 内核,让它通过 hypercall 主动请求 Hypervisor 完成敏感操作。
硬件辅助虚拟化的价值不是“CPU 从此不再模拟设备”,而是让 CPU 原生支持 Guest 特权代码的受控执行。
3.2 VMX root/non-root 不是 Ring -1/Ring 0 的简单上下级
以 Intel VMX 为例,CPU 增加 VMX root 与 VMX non-root 两种运行形态。二者内部仍有原来的 privilege levels。Guest kernel 可以在 non-root 的 Ring 0 运行,但它是否能执行某项操作,还受到虚拟化控制字段约束。
“Ring -1”是便于交流的俗称,不是比 x86 Ring 0 多出来的正式 privilege ring。
3.3 VM Entry、VM Exit 与 VMCS
VMCS 保存并控制一次 vCPU 执行所需的状态,核心可以理解为:
- Guest state:Guest 的控制寄存器、指令位置和其他体系结构状态;
- Host state:退出后恢复到虚拟化层所需的状态;
- execution controls:哪些事件允许直接执行,哪些触发退出;
- exit information:退出原因和相关信息。
运行循环是:
1
2
3
4
5
6
7
8
用户态 VMM 调用 KVM_RUN
→ KVM 准备 vCPU
→ VM Entry
→ Guest 直接执行普通指令
→ 遇到配置为拦截的事件
→ VM Exit
→ KVM 或用户态 VMM 处理
→ 再次进入 Guest
并不是每条 Guest 指令都会 VM Exit。性能优化的重要方向,是让安全的普通执行、内存访问和高频 I/O 尽量留在快速路径。
3.4 vCPU 是状态与执行上下文,不等于物理核心
每个 vCPU 有独立的 Guest CPU 状态。以常见 QEMU/KVM 实现理解,vCPU 通常由 Host 线程驱动,Host scheduler 决定这个线程何时在哪个物理 CPU 上运行。
1
2
3
4
Host scheduler
→ 调度某个 vCPU thread
→ thread 进入 KVM_RUN
→ CPU 执行该 vCPU 的 Guest state
因此:
- 一个 VM 可以有多个 vCPU;
- 多个 vCPU 可以并行运行,也可能在物理 CPU 不足时分时运行;
- Host scheduler 调度的是线程,不是“整个 VM”;
- VM Exit 后通常继续处理当前 vCPU,是否换成另一个 vCPU 由 Host 调度与线程状态共同决定,不是 KVM 在每次退出时主动挑选另一台 VM。
Guest 通过固件表、虚拟 APIC 等机器模型发现多个处理器,并按正常 SMP 启动流程拉起其他 vCPU。vCPU 是虚拟机暴露给 Guest 的 CPU 拓扑,Host thread 是实现它的一种常见方式,两者不是同一个抽象层。
3.5 多 vCPU 还需要中断和时钟机器模型
只创建多个 vCPU thread,不足以让 Guest OS 看到一台可用的多核机器。虚拟化层还要提供固件 CPU 拓扑、Local APIC 或等价中断控制器,以及启动其他 vCPU 所需的机器语义。
中断路径同样需要分层:
1
2
3
4
5
物理设备或 Host 事件
→ 虚拟化层确定目标 vCPU
→ 设置虚拟中断控制器状态或请求硬件注入
→ 目标 vCPU 在可接收时观察到 Guest interrupt
→ Guest 内核的中断处理程序运行
Guest 的时间源与定时器也不能简化为“直接读 Host 时钟”。虚拟化层需要维持 Guest 可观察的时间语义,并在定时器到期时向相应 vCPU 交付虚拟中断。现代硬件可以加速部分中断和定时器路径,但 Host 调度、VM 暂停/恢复、超分和迁移仍会让虚拟时间成为需要显式管理的状态。
因此,vCPU、中断控制器和时钟/定时器应视为同一台虚拟机器模型的不同部件,不是三个孤立优化点。
4. 内存虚拟化:GVA → GPA → HPA
4.1 三种地址不能混在一起
1
2
3
4
GVA: Guest Virtual Address,Guest 进程使用的虚拟地址
GPA: Guest Physical Address,Guest OS 认为的物理地址
HVA: Host Virtual Address,VMM 进程映射 Guest RAM 时看到的地址
HPA: Host Physical Address,真实内存页地址
Guest OS 维护自己的页表,负责 GVA → GPA。虚拟化层还必须负责 GPA → HPA,保证 Guest 只能访问分配给它的 Host 页面。
HVA 是 Host 用户态管理 Guest RAM 时的重要视图,但不是 Guest CPU 最终访问内存所用的地址。Linux 内核仍然控制 HVA/HPA 映射、换页、迁移、合并和大页等 Host 内存行为。
4.2 Shadow Page Table:把两层关系压成一层
没有硬件两阶段翻译时,VMM 可以维护影子页表,让硬件实际使用的页表直接完成 GVA → HPA。Guest 仍以为自己维护 GVA → GPA,虚拟化层需要跟踪 Guest 页表变化,并把结果同步到影子页表。
这种方案不是每次普通 load/store 都进入 VMM。代价主要发生在页表切换、页表写入、缺页和影子映射失效时;同步正确性和 TLB 管理都很复杂。
4.3 EPT/NPT:由硬件完成两阶段翻译
Intel EPT、AMD NPT 属于 two-dimensional paging:
1
2
3
Guest page table: GVA → GPA
EPT/NPT: GPA → HPA
CPU 最终得到: GVA → GPA → HPA
普通内存访问可由 CPU 和 TLB 缓存完成两阶段翻译。缺失的 Guest 页表项由 Guest OS 处理;缺失或违规的第二阶段映射则产生 EPT violation/NPT fault,由虚拟化层处理。
“硬件自动两级翻译”不表示虚拟化层从此不管内存。KVM 仍需建立和失效第二阶段映射,处理权限、内存槽、脏页、回收、迁移和 MMIO 等事件。
5. I/O 虚拟化是一条连续谱
本节只建立设备虚拟化的通用分类;虚拟网卡、TAP、vhost、OVS、overlay 与 container 网络的完整关系,独立整理在《网络虚拟化的数据路径与隔离边界》中。
5.1 传统设备模拟:兼容真实硬件接口
VMM 可以模拟一块 Guest 已有驱动支持的真实设备,例如某种 PCI 网卡或磁盘控制器。Guest 不需要专门理解虚拟化环境,但寄存器访问、通知和设备语义需要 VMM 模拟,兼容性高,路径通常更长。
5.2 VirtIO:为虚拟环境设计的设备规范
VirtIO 是一组虚拟设备及传输规范,不是某个单一实现。它定义 Guest driver 与 device implementation 如何通过配置空间、virtqueue、通知等机制协作。
典型路径是:
1
2
3
4
5
Guest virtio driver
→ virtqueue 中提交 descriptor
→ Host backend 取出请求
→ Host 网络、存储或其他资源
→ 更新 used buffer 并通知 Guest
前后端可能由 QEMU、内核 vhost、vhost-user 进程或其他 VMM 实现。共享内存队列减少了模拟传统设备寄存器和逐请求陷入的成本,但建立队列、通知和部分控制操作仍可能跨越 Guest/Host 边界。
VirtIO 经常称为 paravirtualized I/O,因为 Guest 使用专为虚拟环境设计的驱动并主动遵循协作协议。但这只能描述 I/O 设备路径,不能据此把整台 VM 分类成“半虚拟化系统”。
5.3 设备直通:把真实设备控制面交给 Guest
设备直通把某个物理 PCI function 分配给 VM。Guest 可以使用接近裸机的设备驱动和数据路径,但 Host 仍需建立安全边界:
- 限制设备 DMA 只能访问分配给该 VM 的内存;
- 正确重映射中断;
- 以可隔离的设备/IOMMU group 为分配单位;
- 处理设备 reset、生命周期和迁移限制。
Linux VFIO 利用 IOMMU 提供受保护的用户态设备访问。IOMMU 的关键作用不是“让设备变快”,而是对设备发出的 DMA 地址做翻译和权限隔离,防止设备读写任意 Host 内存。
5.4 SR-IOV:一个设备暴露多个 PCI function
SR-IOV 是 PCI Express 能力。支持它的物理设备提供:
- PF(Physical Function):完整管理能力,通常由 Host PF driver 控制;
- VF(Virtual Function):能力受限但可独立枚举的 PCI function,可以分配给 VM。
SR-IOV 解决的是“一个物理设备如何呈现多个可分配 function”。它不是 CPU 全虚拟化,也不等于 IOMMU:
1
2
3
SR-IOV:设备侧拆分 PF/VF
IOMMU:平台侧隔离和翻译 DMA
VFIO:Linux 向用户态/VMM 暴露安全设备访问的框架
实际直通通常需要设备、固件/主板、IOMMU、Host driver、VFIO/VMM 和 Guest driver 共同支持。VF 接近直通数据路径,但 PF 的配置、资源切分和故障管理仍由 Host 控制。
6. 全虚拟化与半虚拟化:按“Guest 是否必须配合”判断
6.1 全虚拟化强调运行未为 Hypervisor 改写的 Guest
全虚拟化的核心价值是提供足够完整的机器接口,使未经专门改写的 Guest OS 可以运行。它可以由软件翻译实现,也可以使用硬件辅助;“全虚拟化”与“硬件辅助”不是互斥分类。
Guest 能检测到 CPUID hypervisor bit、VirtIO 设备或虚拟厂商字符串,并不会自动改变这个判断。“是否知道自己在 VM 中”只是粗糙直觉;真正关键的是 Guest 的核心执行是否必须改为 hypercall 等虚拟化专用接口才能运行。
6.2 半虚拟化强调 Guest 主动采用虚拟化接口
经典 Xen PV 会修改 Guest 内核,让原本直接操作特权状态的路径改为 hypercall。现代系统更常在局部采用半虚拟化接口,例如:
- VirtIO 设备;
- Hyper-V enlightened I/O 与 synthetic devices;
- paravirtualized clock、interrupt、spinlock 等优化。
因此现实系统通常是分层混合:CPU 使用硬件辅助的全虚拟化,内存使用 EPT/NPT,I/O 使用 VirtIO,少量设备使用直通。给整套系统贴一个“纯全”或“纯半”标签,信息量反而更低。
6.3 VirtIO 既不是充分条件,也不是必要条件
- 不充分:使用 VirtIO 只能证明该设备路径采用半虚拟化协议,不能证明 CPU、内存和所有设备都采用经典 PV。
- 不必要:Xen PV 有自己的 frontend/backend 与 hypercall 体系;半虚拟化并不依赖 VirtIO 这一套具体规范。
7. 用典型系统校准概念
7.1 Xen
Xen 官方将其定义为 bare-metal hypervisor。它位于硬件之上,启动特权控制域 Dom0,再由 Dom0 提供管理和大量设备后端能力。Xen 既有早期 PV guest,也支持依赖硬件辅助的 HVM guest;HVM guest 还可以使用 PV driver 优化 I/O。
Xen 因此不是“只等于半虚拟化”,而是一套同时容纳 PV、HVM 和混合设备路径的虚拟化架构。
7.2 WSL 1 与 WSL 2
Microsoft 官方的关键区分是:WSL 1 使用 system call translation layer;WSL 2 使用真实 Linux kernel,并运行在受管理的 Hyper-V utility VM 中。前者不是传统 VM,后者具有真正的 Guest kernel,只是产品隐藏了大量 VM 生命周期和设备配置。
WSL 2 不要求出现 virtio-* 设备;Hyper-V 有自己的虚拟设备与集成接口。具体的发行版、文件、命令、配置和网络边界独立整理在《WSL 的架构、互操作与使用边界》中。
7.3 Parallels、OrbStack 等桌面产品
在现代 Mac 上,桌面虚拟化产品可以利用 Apple 提供的 Hypervisor/Virtualization 能力运行未为产品本身重写的 Guest OS,并通过专用 Guest Tools、虚拟设备和共享服务优化 I/O 与桌面体验。
“用了专用驱动”不等于整台 VM 是经典半虚拟化,“产品运行在 macOS 应用层”也不等于 CPU 虚拟化由普通用户态代码独立完成。对闭源产品,应把官方承诺、现场观测和合理推断分开,不根据性能表现猜测其私有数据路径。
8. 一套更可靠的分析顺序
遇到一个虚拟化产品或架构时,按下面顺序拆解:
- Guest 是否有独立 kernel,还是共享 Host kernel?
- Guest 指令由硬件辅助直接执行,还是由解释器/二进制翻译执行?
- vCPU 由什么 Host 执行实体驱动,谁负责调度?
GVA → GPA → HPA由影子页表还是两阶段翻译完成?- 每类设备使用传统模拟、半虚拟化协议还是直通?
- DMA 和中断如何隔离?是否使用 IOMMU、VFIO、SR-IOV?
- 哪些属于产品公开承诺,哪些只是现场观测或推断?
这套问题比先问“它是全虚拟化还是半虚拟化”更能还原真实系统。
9. 易混点
| 说法 | 更准确的理解 |
|---|---|
| Guest 知道自己在 VM,所以是半虚拟化 | 能检测 VM 不等于核心执行必须采用 PV 接口 |
| KVM 就是一整套虚拟机 | KVM 提供内核虚拟化 API,完整机器通常还需要用户态 VMM 和设备模型 |
| QEMU 只是网卡、磁盘模拟器 | QEMU 提供完整 system emulation,也能使用 KVM 等 accelerator 或 TCG |
| vCPU 就是物理 CPU | vCPU 是 Guest CPU 状态与执行上下文,常由 Host thread 驱动和调度 |
| VM Exit 时 KVM 选择另一台 VM | Host scheduler 调度 vCPU thread;VM Exit 通常处理当前 vCPU 的事件 |
| EPT 后内存不再需要 KVM 管理 | EPT 加速翻译,KVM 仍管理映射、权限、缺页、回收和脏页 |
| VirtIO 就是半虚拟化 VM | VirtIO 只说明相应 I/O 设备使用 paravirtualized protocol |
| SR-IOV 就是设备直通 | SR-IOV 拆分 PF/VF;VF 还需通过 IOMMU/VFIO 等安全分配 |
| IOMMU 负责创建 VF | VF 由 SR-IOV 设备创建;IOMMU 负责 DMA 翻译与隔离 |
10. 复习索引
- VM 的完整模型是 CPU、内存、I/O 三条虚拟化链路;
- Hypervisor/VMM 术语有重叠,分析职责比争论名称更可靠;
- KVM API 创建 VM/vCPU,
KVM_RUN驱动 vCPU 执行,QEMU 提供机器模型和管理面; - vCPU 常由 Host thread 驱动,真正选择运行线程的是 Host scheduler;
- 内存核心链路是
GVA → GPA → HPA,可由 shadow paging 或 EPT/NPT 完成; - I/O 从传统设备模拟,演进到 VirtIO,再到 VFIO/IOMMU 保护下的设备直通;
- SR-IOV 负责设备拆分,IOMMU 负责 DMA 隔离,两者不是同一种能力;
- 全虚拟化、硬件辅助、半虚拟化 I/O 可以同时存在于一台现代 VM 中;
- 分析闭源产品时,只写官方承诺、可重复观测和明确标边界的推断。
11. 核验入口
- Linux KVM API:VM/vCPU fd、
KVM_CREATE_VCPU、KVM_RUN与用户态边界; - Linux x86 KVM MMU:GVA/GPA/HPA、shadow MMU 与 EPT/NPT;
- QEMU System Emulation Introduction:机器模型、TCG 与 KVM/Xen/HVF/WHPX accelerators;
- VirtIO 1.2 Specification:虚拟设备、transport 与 virtqueue;
- Linux PCI SR-IOV HOWTO:PF、VF、启用和分配模型;
- Linux VFIO:IOMMU 隔离、device assignment 与 IOMMU group;
- Xen Hypervisor Documentation:Xen、Dom0 与 bare-metal 架构;
- Microsoft Comparing WSL Versions:WSL 1 translation layer 与 WSL 2 Linux kernel/Hyper-V VM 边界。