14.1 虚拟机如何共享一台主机
虚拟机把硬件接口虚拟化,容器则共享宿主机内核,并借助命名空间、资源控制和文件系统等机制形成受约束的运行环境。两者都提供隔离,却拥有不同的边界与故障模型。
虚拟机里的操作系统以为自己控制 CPU、内存和设备,真正的机器却同时服务多个 Guest。Hypervisor 的工作不是简单“骗过”操作系统,而是建立一套可执行的资源模型:哪些操作可以直接运行,哪些事件必须退出到 VMM,Guest 地址怎样落到 Host 页面,设备请求又由谁完成。
Linux 容器使用另一条边界,见Namespace、cgroup 与容器隔离。本节只讨论拥有独立 Guest 内核的虚拟机。
1. 虚拟机到底虚拟了什么
一台典型 VM 会看到:
- 一个或多个虚拟 CPU(vCPU);
- 一段 Guest physical memory;
- 中断控制器与计时器;
- 磁盘、网卡、控制台等虚拟设备;
- 固件或启动协议;
- 独立运行的 Guest 内核。
Guest 进程仍先由 Guest 内核管理。Host 侧的 hypervisor/VMM 再把 vCPU 映射到物理 CPU 时间,把 Guest physical address 映射到 Host memory,并处理设备访问。
Guest 应用
↓ system call
Guest 内核
↓ virtual CPU / memory / device
Hypervisor + 用户态 VMM
↓
Host CPU / memory / device“Type 1 裸机”与“Type 2 托管式”有助于入门,但现代实现的边界并不总是整齐。例如 KVM 把核心虚拟化能力放在 Linux 内核里,QEMU 等用户态进程负责设备模型与管理;很难只用一张两层图说完。
2. CPU 虚拟化:直接执行与退出处理
理想情况下,Guest 的普通算术、分支和内存指令直接在物理 CPU 上执行。只有需要 hypervisor 介入的事件才发生 VM exit,例如某些受控指令、配置要求拦截的寄存器访问、外部中断或设备访问。
硬件虚拟化扩展提供了两套执行上下文。以 Intel 术语为例:
VMX root operation
Host/VMM 在这里控制 VM
│ VM entry
▼
VMX non-root operation
Guest 可在自己的 ring 0 运行
│ 受控事件触发 VM exit
▼
VMM 读取退出原因、模拟或处理,再次进入 Guestroot/non-root 不是传统 ring 0/ring 3 的替代品。两种 operation 内部仍有各自的特权级。Guest 内核可以处于 non-root 的 ring 0,但不能因此取得 Host 的完整控制权。
VMM 通过控制结构配置 Guest 状态、Host 恢复状态和退出条件。并非所有“敏感操作”都必须每次退出;现代硬件可以直接维护部分虚拟状态,VMM 也会根据安全性与性能选择拦截范围。
硬件扩展之前
经典虚拟化理论希望敏感指令在权限不足时陷入,让 VMM 有机会模拟。早期 x86 存在一些不满足这种简单 trap-and-emulate 模型的行为。实现者采用过二进制翻译、ring deprivileging 和 paravirtualization 等技术,把危险指令改写或让 Guest 主动调用 hypervisor。
这段历史说明硬件扩展解决的是可控执行边界,并不意味着“以前完全不能虚拟化”,也不能用一个固定性能损失概括所有旧实现。
3. vCPU 仍然要被 Host 调度
在 KVM 等实现中,每个 vCPU 最终是 Host 调度器管理的执行实体;裸机 hypervisor 则可能使用自己的调度器。若 VM 配置了 8 个 vCPU,并不表示它永久占有 8 个物理核心:
- Host 可能把多个 vCPU 分时运行在同一物理 CPU 上;
- vCPU 可能迁移,破坏缓存与 NUMA 局部性;
- Host 过量分配后,Guest 可运行线程会等待物理 CPU;
- vCPU 持有 Guest 自旋锁时被换出,会放大其他 vCPU 的自旋;
- Host 还要运行 I/O 后端、管理进程和其他工作负载。
Guest 里的 CPU 利用率只描述它看到的时间。性能诊断还要观察 Host 调度、steal time、vCPU 拓扑、NUMA 放置和超额订阅。
固定绑核可能改善抖动,却降低调度弹性;是否值得取决于延迟目标、同机干扰和硬件拓扑。
4. 两阶段地址翻译
Guest 进程产生 Guest virtual address(GVA)。Guest 页表把它翻译成 Guest physical address(GPA),hypervisor 再把 GPA 映射到 Host physical address(HPA):
GVA
│ Guest page table
▼
GPA
│ EPT / NPT 等第二阶段页表
▼
HPAIntel 常称第二阶段页表为 EPT,AMD 常称 NPT。CPU 的 TLB 可以缓存组合后的翻译,避免每次访问都完整遍历两套多级页表。
影子页表与嵌套页表
没有硬件第二阶段翻译时,VMM 可以维护从 GVA 直接到 HPA 的 shadow page table。Guest 改页表时,VMM 必须捕获并同步影子映射,路径复杂且容易产生退出。
嵌套页表让硬件在页表遍历过程中继续翻译 Guest 页表项所在的 GPA。它减少了很多页表维护退出,但 TLB miss 时的组合遍历更昂贵。因此大页、TLB 命中率和内存局部性仍然重要。
不要背“最多固定多少次内存访问”作为性能结论。页表级数、页大小、缓存命中、硬件 page-walk cache 与架构都会改变实际代价。
5. 内存不是只做一次地址映射
VM 内存管理还要面对:
按需分配。 VMM 可以先保留 Guest 地址空间,等 Guest 真正触碰页面时再取得 Host 物理内存。
内存超量承诺。 多个 VM 的配置内存之和可以超过物理内存,前提是实际活跃集没有同时达到上限。压力突增时可能发生 Host swap、回收失败或 OOM,尾延迟会急剧恶化。
balloon。 Guest 内驱动主动占用并交还一批页面,让 Host 知道哪些 Guest 内存可以回收。它需要 Guest 配合,不等于透明地夺走任意页面。
大页。 大页减少 TLB 与页表开销,但使分配、碎片、迁移与回收更粗粒度。
NUMA。 vCPU 在一个 socket 运行而内存落在另一个 NUMA 节点,会增加访问成本。vCPU 与内存策略要一起设计。
脏页跟踪。 实时迁移和增量快照要知道 Guest 修改了哪些页面。跟踪粒度与写密集程度会影响迁移能否收敛。
6. I/O 虚拟化的三条常见路径
设备模拟
VMM 呈现一个 Guest 已有驱动支持的硬件型号。Guest 访问端口或 MMIO 寄存器时,VMM 模拟设备状态机。
优点是兼容旧 Guest;代价是设备模型复杂,频繁访问可能导致更多退出和用户态处理。设备模型本身也是安全边界的一部分,需要严格校验 Guest 提供的描述符、长度和状态转换。
半虚拟化设备
virtio 一类接口不假装传统物理设备,而是让 Guest 驱动通过共享队列提交描述符。后端批量处理,再通过通知机制更新 Guest:
Guest virtio driver
↓ descriptors
shared virtqueue
↓ notification
QEMU / vhost / hardware backend
↓
physical device批处理能减少每个请求的陷入和模拟开销,但“virtio 就是零拷贝”并不成立。数据是否复制、在哪个网络或块层排队,取决于后端、缓冲区和卸载配置。
设备直通
VFIO 等机制可以把物理设备或 SR-IOV virtual function 交给 VM。IOMMU 把设备 DMA 限制在授权的 Host 页面映射内:
Guest driver ── MMIO ──> device
│
└─ DMA address ──> IOMMU translation ──> allowed Host pages直通缩短数据路径,却增加迁移、快照、复位隔离、拓扑与设备共享的难度。IOMMU 是必要隔离组件之一,不表示任意设备固件都自动可信。
7. 中断与时钟也要虚拟化
设备完成请求后要通知 Guest。传统方案可能让物理中断先到 Host,再注入虚拟中断;现代硬件可以加速其中一些路径。若中断频率过高,合并、批处理和 polling 可能比逐个退出更合适。
时间更棘手。vCPU 被 Host 暂停后,Guest 的墙上时钟、单调时钟和 CPU 时间应该怎样变化?迁移到另一台 Host 后,计时源是否连续?Hypervisor 与 Guest 通常通过 paravirtual clock 等机制协调。分布式应用仍应使用正确的时钟语义,不能把虚拟机时钟当作天然精确。
8. 虚拟机与容器比较的是边界
| 维度 | 虚拟机 | Linux 容器 |
|---|---|---|
| 内核 | 每个 Guest 有自己的内核 | 进程共享 Host 内核 |
| 抽象 | 虚拟硬件 | 隔离的内核资源视图 |
| 系统兼容 | 可运行不同 Guest OS,受虚拟硬件支持限制 | 用户态必须兼容 Host 内核 ABI |
| 启动路径 | 固件/引导、Guest 内核、用户空间 | 创建受隔离和限制的进程 |
| 主要隔离边界 | Hypervisor、虚拟设备、管理层 | Host 内核、runtime、namespace/cgroup 配置 |
| 资源基线 | 通常更高 | 通常更低 |
这张表不能推导“VM 一定安全”或“容器一定快”。虚拟设备漏洞、管理面权限和共享硬件侧信道都可能跨 VM 边界;容器则需要 user namespace、capabilities、seccomp 和 LSM 等纵深限制 Host 内核攻击面。
选择时问的是需要哪个内核边界、哪类设备、怎样迁移与运维,而不是背一个启动时间或性能百分比。
9. 常见误解
“KVM 就是一台完整虚拟机。” KVM 提供内核侧 vCPU 与内存等基础接口。完整 VMM 还要准备固件、设备、中断、镜像、迁移与管理。
“所有 Guest 指令都会经过 hypervisor。” 普通指令通常直接执行;受控事件才退出。
“嵌套页表消除了内存虚拟化开销。” 它减少页表同步退出,但组合翻译、TLB miss 和 NUMA 仍有成本。
“分配 16 vCPU 就得到 16 核算力。” vCPU 是可调度实体,物理 CPU 时间仍由 Host 分配。
“设备直通总是最好。” 它以共享、可迁移性和运维复杂度换取较短数据路径。
10. 小结
虚拟机的核心是受控复用:
- CPU 让普通 Guest 指令直接执行,在受控事件上 VM exit;
- vCPU 仍服从 Host 调度,超额订阅会影响延迟;
- GVA 经 Guest 页表和第二阶段页表到达 HPA;
- 内存还涉及 balloon、脏页、NUMA 与大页;
- I/O 可选择设备模拟、virtio 或直通;
- 独立 Guest 内核形成了不同于容器的隔离边界。