3、KVM 基础:KVM 架构解析,内核模块与 QEMU 的协作关系

好,咱们进入虚拟化技术的核心环节。KVM,全称 Kernel-based Virtual Machine,说白了就是 Linux 内核自带的那套虚拟化方案。很多刚接触的朋友会问:KVM 和 QEMU 到底是什么关系?为什么总是一起出现?

我个人习惯把 KVM 理解为「内核里的加速器」,而 QEMU 是「用户态的设备模拟器」。它俩配合,才能跑起一台完整的虚拟机。今天咱们就把这套协作机制彻底拆开看看。

3.1 KVM 内核模块:虚拟化的「发动机」

KVM 本身是一个内核模块。你装好 Linux 后,执行 modprobe kvm 就能加载它。如果是 Intel 的 CPU,还需要加载 kvm_intel;AMD 的话就是 kvm_amd

它的核心作用是什么?让 CPU 直接支持虚拟化。没有 KVM 时,QEMU 只能通过软件模拟 CPU 指令,慢得让人抓狂。有了 KVM,虚拟机里的指令可以直接在物理 CPU 上跑,性能接近原生。

关键点:KVM 把 Linux 内核变成了一个 Type-2 的 Hypervisor。但它只处理 CPU 和内存的虚拟化,I/O 设备它不管。

我记得第一次调 KVM 性能时,发现虚拟机 CPU 占用率居高不下。查了半天,原来是宿主机没开启硬件虚拟化支持。执行 cat /proc/cpuinfo | grep vmx 一看,啥都没有。嗯,这是个经典坑。

3.2 QEMU:用户态的「设备工厂」

QEMU 负责模拟虚拟机需要的各种设备:网卡、磁盘、USB 控制器、显卡等等。它运行在用户态,通过 /dev/kvm 这个字符设备和内核里的 KVM 模块通信。

你可以把 QEMU 想象成一个「硬件模拟器」。它用软件模拟出你指定的设备,然后告诉 KVM:「嘿,这个虚拟机的内存布局是这样的,CPU 核心数是 4 个,你帮我管好 CPU 调度。」

为什么需要 QEMU?因为 KVM 只管 CPU 和内存,它不关心你虚拟机里装的是 Windows 还是 Linux,也不管你用的是什么网卡。这些「外设」的模拟,全交给 QEMU 处理。

我的经验:QEMU 的设备模拟性能其实一般。生产环境中,我们通常会用 virtio 半虚拟化驱动,或者直接做 PCIe 直通,把物理设备直接分配给虚拟机。这样能大幅提升 I/O 性能。

3.3 协作流程:一条命令背后的故事

当你执行 qemu-system-x86_64 -m 2048 -smp 4 -hda ubuntu.qcow2 时,背后发生了什么?

  1. QEMU 启动:解析命令行参数,分配内存,创建线程。
  2. 打开 /dev/kvm:QEMU 通过这个设备文件,向内核请求 KVM 服务。
  3. 创建虚拟机:KVM 在内核空间创建一个虚拟机的数据结构,分配 vCPU 和内存映射。
  4. 设置内存:QEMU 把虚拟机的物理内存映射到宿主机的虚拟地址空间。KVM 负责维护页表。
  5. 启动 vCPU:QEMU 调用 KVM 的 KVM_RUN ioctl,让 vCPU 开始执行指令。
  6. 陷入与返回:当虚拟机执行 I/O 指令或触发异常时,CPU 会「陷入」到 KVM 模块。KVM 判断:如果是简单的指令(比如读写寄存器),它自己处理;如果是复杂的设备操作,就返回给 QEMU 模拟。

这个「陷入-返回」机制,就是 KVM 和 QEMU 协作的核心。说白了,KVM 处理「快」的事情(CPU 指令、内存访问),QEMU 处理「慢」的事情(设备模拟)。

避坑指南:我曾经遇到一个性能问题,虚拟机跑数据库时频繁卡顿。用 perf top 一看,大量时间花在 kvm_exit 上。原因是虚拟机里用了老旧的 e1000 网卡,每次网络包都要陷入到 QEMU 模拟。换成 virtio-net 后,性能直接翻倍。

3.4 关键数据结构与接口

理解协作关系,还得看几个关键的数据结构。KVM 在内核中维护了这些对象:

数据结构 作用 对应 QEMU 侧
struct kvm 代表一个虚拟机实例 QEMU 的 KVMState
struct kvm_vcpu 代表一个虚拟 CPU QEMU 的 CPUState
struct kvm_memory_slot 描述虚拟机的内存区域 QEMU 的内存映射表
struct kvm_irqchip 管理虚拟中断控制器 QEMU 的 PIC/APIC 模拟

QEMU 通过 ioctl 系统调用操作这些对象。常用的 ioctl 包括:

  • KVM_CREATE_VM:创建虚拟机
  • KVM_CREATE_VCPU:创建 vCPU
  • KVM_SET_USER_MEMORY_REGION:设置内存映射
  • KVM_RUN:启动 vCPU 执行
  • KVM_GET_DIRTY_LOG:获取脏页日志(用于热迁移)

你想想看,这些接口设计得其实很简洁。KVM 只暴露最核心的 CPU 和内存操作,其他一切交给 QEMU。这种「各司其职」的设计,让 KVM 保持了内核模块的轻量和稳定。

3.5 实际项目中的协作调优

我在做新能源调度系统时,需要在一台物理机上跑多个实时控制虚拟机。当时遇到一个问题:虚拟机里的实时任务偶尔会丢中断。

排查后发现,QEMU 模拟的中断控制器延迟太高。KVM 虽然能快速处理 CPU 指令,但中断从设备模拟到 vCPU 的路径太长了。解决方案是启用 KVM 的 in-kernel irqchip,把中断控制器也放到内核里处理。

配置方法很简单:启动 QEMU 时加上 -machine kernel_irqchip=on。这样中断路径从「设备→QEMU→KVM→vCPU」变成了「设备→KVM→vCPU」,延迟降低了一个数量级。

总结一下:KVM 和 QEMU 的协作,本质是「内核态加速 + 用户态模拟」的分工。KVM 负责 CPU 和内存的虚拟化,QEMU 负责设备模拟。两者通过 /dev/kvm 和 ioctl 通信。理解这个模型,你就能在遇到性能问题时,快速定位瓶颈是在内核侧还是用户侧。

嗯,下一节我们会深入 KVM 的 CPU 虚拟化实现,看看 vCPU 到底是怎么在物理核上切换的。到时候你会更清楚,为什么 KVM 能跑出接近物理机的性能。