virt-report虚拟化社区动态

CPU 虚拟化:执行方式、隔离与调度

本文由 OpenAI Codex (GPT-6-Astra) 辅助编写。

一、CPU 虚拟化的执行环境与权限边界

CPU 虚拟化为客户机提供独立的处理器状态和指令执行环境,同时保留宿主对真实资源的控制。一段程序做整数加法,只需 CPU 按指令修改寄存器;操作系统内核还会切换页表、管理中断、访问设备。如果让客户机内核直接控制宿主的这些资源,它就可能覆盖宿主地址空间,或影响其他虚拟机;若把它简单当作普通用户程序运行,原本合法的内核操作又会因权限不足而失败。

一个 vCPU 表示客户机可见的一套寄存器、指令位置、特权状态和异常行为;物理 CPU 则在调度时实际执行它。客户机可以看见 4 个 vCPU,宿主仍能把这 4 个执行线程与其他任务共享调度。指令语义、资源隔离和执行机会因此是三个要分别解决的问题。

本文以 x86 系统虚拟化为主,比较软件执行与硬件辅助的实现,再跟踪一段程序中的普通计算、能力查询、设备访问和空闲操作,说明它们的处理位置与开销。

源码版本:QEMU 10.2.0、Linux 6.18。

二、解释执行、动态翻译与硬件辅助

解释执行、动态翻译与硬件辅助
三种方式都要保持客户机寄存器、权限和异常的含义。解释器逐条执行语义,TCG 缓存翻译后的宿主代码,KVM 让硬件运行客户机并处理需要接管的事件。
三种方式都要保持客户机寄存器、权限和异常的含义。解释器逐条执行语义,TCG 缓存翻译后的宿主代码,KVM 让硬件运行客户机并处理需要接管的事件。 打开原图
Mermaid 源码
flowchart TB
  accTitle: 解释执行、动态翻译与硬件辅助
  accDescr: 客户机代码可以由解释器逐条执行语义,可以经 TCG 形成缓存的宿主代码,也可以在 KVM 配置的硬件环境中直接执行。三种方法都必须保持寄存器、权限和异常的客户机语义。
  G["客户机指令与 CPU 状态"] --> I["解释执行<br/>取指 → 解码 → 更新模拟状态"]
  G --> T["TCG<br/>首次翻译 → 缓存 TB → 执行宿主代码"]
  G --> K["KVM + 硬件辅助<br/>执行客户机 → 需要时 VM exit"]
  I --> S["保持客户机可见的寄存器、权限与异常"]
  T --> S
  K --> S
  classDef guest fill:#e4f1ec,stroke:#438575,color:#173e34;
  classDef method fill:#eaf0f6,stroke:#8198ac,color:#263c4c;
  classDef semantic fill:#fff4db,stroke:#b89a55,color:#59491e;
  class G guest;
  class I,T,K method;
  class S semantic;

1. 解释执行:逐条读懂并实现指令

最直接的方案是取出客户机指令,解码,再用宿主程序更新模拟寄存器和内存。例如客户机加法由解释器完成运算并更新客户机标志位;客户机修改页表基址,也只修改模拟状态。访问都经过软件控制,便于隔离和跨架构实现,但热点循环每次都要重新取指、解码和分派,管理成本很高。

2. TCG:把重复解码变为可复用代码

QEMU 的 Tiny Code Generator(TCG)将一段客户机指令翻译为中间表示,再生成宿主机器码,缓存为 Translation Block(TB,翻译块)。普通运算成为宿主代码,复杂操作调用实现相应语义的 helper。下一次进入相同代码和匹配的 CPU 状态时,可以复用 TB;满足条件的块还能直接连接,减少返回主循环查找的次数。

翻译要付出首次生成和缓存管理成本。客户机修改代码后,旧 TB 必须失效;中途发生异常时,还需恢复准确的客户机指令位置。TCG 因而适合不同指令集之间的执行、开发调试及缺少硬件辅助的环境,它与 KVM 在当前 QEMU 中并存。TB 状态匹配与直接连接

3. 硬件辅助:保留客户机权限,分开宿主控制

Intel VT-x、AMD-V 提供受宿主配置控制的客户机执行环境。客户机内核仍可处于自己的高权限级;处理器按虚拟化控制决定哪些行为直接完成,哪些触发 VM exit(客户机退出),把控制权交回宿主。客户机从应用进入自身内核,与离开客户机环境进入宿主,是两类不同切换。

以 Intel VT-x 为例,VMX root / non-root 区分宿主与客户机执行环境,ring 0 / ring 3 区分各自环境内的权限级。这是两个维度:

权限级VMX root:宿主环境VMX non-root:客户机环境
ring 3QEMU 等宿主用户程序客户机应用
ring 0Linux 内核与 KVM客户机操作系统内核

客户机应用执行系统调用,可以在 non-root 内进入自己的 ring 0;是否同时触发退出取决于配置和具体执行事件。VM exit 则把控制权交给宿主内核。客户机 ring 0 的权限只适用于它的执行环境,不允许它自行改写宿主配置的虚拟化控制。

VMCS 的状态区域与执行控制
VMCS 的客户机状态区域保存客户机执行现场,宿主状态区域提供退出后的执行环境,控制字段决定进入、退出以及需要拦截的操作。通用寄存器等未由硬件完整交接的状态仍需软件保存。
VMCS 的客户机状态区域保存客户机执行现场,宿主状态区域提供退出后的执行环境,控制字段决定进入、退出以及需要拦截的操作。通用寄存器等未由硬件完整交接的状态仍需软件保存。 打开原图
Mermaid 源码
flowchart TB
  accTitle: VMCS 的执行现场与控制条件
  accDescr: KVM 配置 VMCS。进入客户机时装入客户机状态,发生需要退出的事件后保存客户机状态并装入宿主状态,KVM 再处理退出原因。图不表示 VMCS 保存所有寄存器。
  K["KVM 配置 VMCS"] --> C["执行控制<br/>拦截条件与事件控制"]
  K --> G["客户机状态<br/>RIP / RSP / CR3 / 段状态"]
  K --> H["宿主状态<br/>退出入口 / 宿主栈 / 地址空间"]
  G -->|VM entry| V["客户机执行<br/>自身 ring 0 或 ring 3"]
  C -. "决定哪些事件退出" .-> V
  V -->|VM exit:保存相应现场| G
  V -->|装入宿主状态| H
  H --> R["KVM 处理退出原因"]

VMCS(虚拟机控制结构)把这条边界具体化:客户机区域记录进入时的执行现场,宿主区域指定退出后的执行环境,控制字段决定哪些事件需要接管。硬件负责规定的状态交接,其余通用寄存器等仍由 KVM 保存。AMD 的 VMCB 承担相应职责。理解总流程时,可以先把它们看作“执行现场+拦截规则”;具体交接见KVM 执行循环及进入与退出汇编。

KVM 在 Linux 内核中建立并维护这个执行环境,QEMU 的 vCPU 线程通过 KVM_RUN 进入它。硬件退出后,KVM 能处理的事件在内核中完成,再次进入客户机;只有需要用户态设备模型或管理参与的情况才返回 QEMU。因此 VM exit 次数与 KVM_RUN 返回次数通常不同。

三、计算、CPUID、设备读取与等待的执行示例

假设客户机先计算 3 + 4,查询 CPUID,再读取一个由 QEMU 模拟的设备寄存器,最后执行 HLT 等待工作。设备应返回 0x12345678。客户机看到同一段程序,执行这些步骤的位置则随加速器改变:

客户机操作TCG 路径Intel VMX / KVM 路径
计算 3 + 4QEMU 用户态执行翻译后的宿主代码,更新客户机状态物理 CPU 在客户机环境直接执行,结果为 7
查询 CPUIDQEMU 按虚拟 CPU 配置模拟能力退出到宿主内核,由 KVM 填写结果后继续客户机
读取用户态模拟的设备访存辅助路径进入 QEMU 设备回调KVM_RUN 返回 QEMU;设备回调填写 kvm_run.mmio,下一次进入 KVM 完成读取
HLT 等待QEMU 记录客户机停止状态,由执行循环处理等待常见内核 LAPIC 路径由 KVM 轮询或阻塞,Linux 调度宿主线程

本例的加法结果是 7,设备读取结果是 0x12345678;选用 TCG 或 KVM 都应保持这些结果。区别在于执行每一步时,宿主做了多少翻译、退出、设备处理与调度工作。优化需要减少这些额外工作,同时保持客户机可观察的行为。QEMU 的 KVM 执行循环给出了用户态处理分支,退出总数本身无法说明哪一步最慢。

四、代码复用、退出处理与并行执行的演进

动态翻译把逐条解释中的重复工作移到首次翻译,后续通过缓存与块连接降低分派成本。硬件辅助让兼容架构的指令直接执行,并把权限隔离交给明确的硬件边界。两条路线解决的条件不同:跨架构执行仍需要翻译,硬件辅助也仍需要设备模型和异常处理。

继续优化时,TCG 用多线程执行不同 vCPU,需要同时维护客户机内存顺序、共享设备锁和翻译失效。KVM 则可用共享队列与事件通知减少设备往返,或由硬件加速部分中断处理。这些改进把工作移动到不同层次,无法消除后端处理与状态同步。

虚拟 CPU 型号还有兼容性取舍。向客户机暴露更多宿主特性,可能改善本机计算性能,却减少能接收该虚拟机的迁移目标。固定兼容的 CPU 能力集合有利于跨宿主调度,代价是不能使用其中较新机器的全部能力。

五、按执行成本评价方案

主要成本改进着力点需要一起检查的代价
取指、解码与翻译复用 TB、连接热块,条件允许时采用 KVM代码失效、首次执行成本与架构兼容性
设备访问和频繁退出批处理、virtio 队列、内核或独立后端后端 CPU、通知延迟与设备状态协调
空闲与唤醒等待平衡短暂轮询和阻塞,调整调度与 CPU 放置尾延迟、物理 CPU 占用与其他虚拟机的公平性

扩展 vCPU 数量的效果取决于业务并行度与宿主可提供的运行时间。客户机内观察任务和锁等待,宿主侧观察 vCPU 线程调度、KVM 退出及设备后端;两侧数据对应同一段业务负载后,再比较吞吐、尾延迟和 CPU 消耗。客户机中的 CPU 利用率反映其自身调度视图,宿主线程消耗还可能包含设备处理和轮询。

六、进一步阅读

七、参考资料