本页目录

KVM 架构

覆盖: KVM 作为 Linux hypervisor → VMX/SVM 硬件虚拟化 → vCPU 生命周期 → VM exit 处理 → KVM + QEMU 协作模型 → /dev/kvm ioctl API 内核版本: 2.6.20 ~ 6.x

概述

KVM (Kernel-based Virtual Machine) 把 Linux 内核转变为一个 type-1 hypervisor——利用 Intel VMX 或 AMD SVM 硬件虚拟化扩展,让 guest 在 VMX non-root 模式下直接运行在物理 CPU 上。每个 VM 是用户态的一个 QEMU 进程,每个 vCPU 是进程中的一个线程。

架构分层

KVM 架构分层:QEMU 用户态 · 内核态 KVM QEMU(用户态) 设备模拟 virtio / VGA / 存储 / 网络 虚拟机管理 live migration / snapshot KVM ioctl /dev/kvm ioctl 系统调用穿透用户态 → 内核态 内核(KVM) kvm.ko 架构无关 core kvm-intel.ko VMX 支持 kvm-amd.ko SVM 支持 QEMU 只通过 /dev/kvm ioctl 与内核对话,不直接碰虚拟化寄存器; kvm.ko 是架构无关核心,kvm-intel.ko / kvm-amd.ko 分别对接 VMX / SVM。

VM Entry / VM Exit

VM Entry / VM Exit:guest 运行与内核处理的循环 Guest 运行 VMLAUNCH(VMX) / VMRUN(SVM) → 进入 VMX non-root 模式 guest 直接执行在物理 CPU 上 (直到触发 VM exit) VM Exit(root 模式) guest 执行敏感指令 (HLT / IO / EPT violation …) KVM 读 exit reason 分发处理: IO_INSTRUCTION → 模拟 IO 指令 EPT_VIOLATION → 处理 guest 页表变更 CPUID → 模拟 CPUID HLT → idle injection MSR_READ/WRITE → 模拟 MSR 访问 处理完 → VMRESUME 回到 guest 敏感指令触发 → 退出到 root 处理完 → VMRESUME 回到 guest VM Exit 是 KVM 的核心循环:guest 尝试敏感操作 → 陷入内核 → KVM 软件模拟 → VMRESUME 恢复; 读 exit reason 后按类型分发处理,是虚拟化里 guest/host 切换开销的主要来源。

vCPU 线程模型

// 每个 vCPU = 一个 QEMU 线程, 执行循环:
while (running) {
    ioctl(vcpu_fd, KVM_RUN, 0);
    // 返回: VM exit
    switch (vcpu->kvm_run->exit_reason) {
    case KVM_EXIT_IO:       handle_io(vcpu);       break;
    case KVM_EXIT_MMIO:     handle_mmio(vcpu);     break;
    case KVM_EXIT_IRQ_WINDOW_OPEN: inject_interrupt(vcpu); break;
    // ...
    }
}

Scheduler 交互

// KVM 通过 preempt notifier 知道 vCPU 何时被抢占:
// → kvm_sched_out(): vCPU 被调度出去了
// → kvm_sched_in():  vCPU 获得了 CPU
// 用于: steal time accounting, TSC 偏移计算

/dev/kvm 接口

// 创建 VM:
int kvm_fd = open("/dev/kvm", O_RDWR);
int vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);

// 注册内存 (guest 物理地址空间):
struct kvm_userspace_memory_region region = {
    .slot = 0, .guest_phys_addr = 0, .memory_size = 1ULL << 30,
    .userspace_addr = (__u64)guest_ram_ptr, .flags = 0 };
ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &region);

// 创建 vCPU:
int vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, cpu_id);

// 获取 KVM 版本/能力:
int version = ioctl(kvm_fd, KVM_GET_API_VERSION, 0);
struct kvm_cpuid2 cpuid = { .nent = N }; 
ioctl(kvm_fd, KVM_GET_SUPPORTED_CPUID, &cpuid);

中断注入

// KVM 将中断注入给 guest:
//   1. 设置 VMCS (VMX) 或 VMCB (SVM) 的中断信息字段
//   2. 设置 interrupt window → VM entry 后 guest 立即响应
//   3. 如果 vCPU 没在跑 (被调度出去) → 设 kick flags

// 中断来源:
//   - virtio 设备 (msix)
//   - 模拟设备 (8259A PIC, IOAPIC)
//   - IPI (guest 内多 vCPU)
//   - timer (hrtimer→KVM timer)

参考

  • 源码⁠: virt/kvm/kvm_main.c (核心), arch/x86/kvm/vmx/vmx.c (Intel VMX), arch/x86/kvm/svm/svm.c (AMD SVM)
  • 内核文档⁠: Documentation/virt/kvm/
  • LWN: "KVM: the Linux virtual machine monitor"

关键词: KVM, VMX, SVM, vCPU, VM exit, VMLAUNCH, VMRESUME, /dev/kvm, KVM_RUN, interrupt injection