跳转到内容

KVM 2007 — 把 Linux 内核本身变成 hypervisor

是什么

KVM(Kernel-based Virtual Machine)是 2007 年加进 Linux 主线的一个内核模块,它做的事一句话:让 Linux 自己变成 hypervisor

日常类比:旅馆要开”包间出租”业务。VMware / Xen 的做法是另起一栋楼,自己再雇一套保安、保洁、前台。KVM 的做法是——旅馆现成的保安、保洁、前台都在,给前台多发一把”包间钥匙”,他兼着做就行了。

具体到 Linux:

  • 每台虚拟机 = 宿主机上的一个普通进程
  • 每个 vCPU = 这个进程里的一个线程
  • 虚拟机的物理内存 = 进程通过 mmap 拿到的一段虚拟内存
  • 调度、内存管理、cgroups 隔离、信号、ptrace —— 全部复用 Linux 现成的

加载 KVM 模块后,多出一个设备文件 /dev/kvm,用户态程序(一般是 QEMU)通过 ioctl 三件套 KVM_CREATE_VM / KVM_CREATE_VCPU / KVM_RUN 就能跑虚拟机。

为什么重要

不理解 KVM 的取舍,下面这些事都没法解释:

  • 为什么今天 OpenStack / oVirt / Proxmox / 公有云 IaaS 大多基于 KVM 而不是 Xen
  • 为什么 AWS 的 Firecracker(Lambda 底座)、Google 的 gVisor 路线、WSL2 都走”小型 KVM 用户态”这条路
  • 为什么”hypervisor”和”操作系统”在 Linux 世界已经融成一回事——top 里看到的 qemu 进程就是一台云主机
  • 为什么 Intel VT-x / AMD-V 这些 2005 年的硬件扩展,是 KVM 能在 6 页论文里讲完的前提

KVM 之前,hypervisor 是个”独立王国”(Xen / ESX);KVM 之后,hypervisor 是 Linux 的一个子系统

核心要点

KVM 的核心设计可以拆成 三条

  1. 靠硬件,不靠翻译:Intel VT-x / AMD-V 引入了一种新的 CPU 模式(VMX root / non-root),让 guest 的特权指令直接被硬件捕获。KVM 不再需要 VMware 早期那套”扫描指令、动态翻译”的复杂逻辑,论文里反复强调这一点是 KVM 能做小的根本原因。

  2. 三层模式切换:CPU 在三种模式间转——guest 模式(跑虚拟机里的代码)/ 内核模式(KVM 模块处理捕获事件)/ 用户模式(QEMU 模拟 IO 设备)。一次 IO 操作可能 guest → 内核 → 用户态来回弹一次。

  3. 职责切分:KVM 内核模块只管 CPU 和内存;网络、磁盘、显卡这些 IO 设备的模拟全部丢给用户态的 QEMU。这条线划得很清,让 KVM 模块本身保持小(论文时代约一万多行 C),bug 面小、上游审核容易过。

实践案例

案例 1:跑一台虚拟机的最小代码

用户态程序大致这样(伪代码):

int kvm_fd = open("/dev/kvm", O_RDWR);
int vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);
ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &mem); // 把一段 mmap 映射成 guest 物理内存
int vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);
while (1) {
ioctl(vcpu_fd, KVM_RUN, 0); // CPU 切到 guest 模式跑
// 返回时检查 exit_reason:是 IO?是 halt?是 page fault?
}

50 行 C 就能跑一台最小 VM。KVM_RUN 返回时,exit_reason 字段告诉用户态”为啥被踢出来”,QEMU 据此模拟相应行为。

案例 2:影子页表为什么是个坎

guest OS 自己也维护一套页表(guest 虚拟 → guest 物理)。但 MMU 只认一套页表,所以 KVM 必须合成一份”guest 虚拟 → 宿主物理”的影子页表给硬件用。

guest 每次改自己的 CR3(改页表根)→ KVM 拦截 → 重建影子页表。这个过程很贵

后来 Intel EPT(2008)/ AMD NPT 引入”二级地址翻译”,硬件直接做两层映射,影子页表就可以扔了。这是 KVM 性能起飞的拐点——但论文写于 2007 年,还在影子页表时代。

案例 3:今天的延伸——Firecracker

AWS 的 Firecracker(2018)是”砍到极致的 KVM 用户态”:

  • 扔掉 QEMU 庞大的设备模型
  • 只保留必要的 virtio 块设备 / 网卡 / serial
  • 启动时间从秒级压到 125ms

这就是 KVM 设计哲学的延续——复用内核 + 用户态尽可能薄。Lambda 一台机器跑成千上万的轻量 VM 都靠它。

踩过的坑

  1. 影子页表性能崩塌:guest 频繁改页表(fork 多 / 进程切换密集)时,影子页表维护代价压过虚拟化收益。EPT/NPT 出现前,KVM 在 mmap-heavy 负载上打不过 Xen。

  2. IO 性能瓶颈在用户态:早期 KVM 网络/磁盘 IO 走”guest → 内核 → QEMU → 内核”四次切换。后来 vhost-net / vhost-scsi 把数据路径下沉回内核态才解决。

  3. 嵌套虚拟化(nested virt):VM 里再跑 VM 这件事,KVM 早期不支持,后来加上但实现极复杂、性能损失大。直到今天云厂商对外开嵌套虚拟化都很谨慎。

  4. “复用 Linux 调度”是把双刃剑:好处是不重写;坏处是 vCPU 抢占、CFS 调度延迟、NUMA 亲和这些问题,KVM 必须适应 Linux 调度器的脾气,而不能像 Xen 那样自己写一个。

  5. KVM 安全边界 = Linux 内核安全边界:KVM 跑在 ring 0,意味着 KVM 的任意一个内核漏洞都可能让 guest 逃逸到宿主。这是 Type-2 路线的固有代价,VMware ESX / Xen 的 Type-1 攻击面会小一些。

  6. 设备直通(VFIO)门槛:把宿主物理 GPU/网卡直接交给 guest 用,需要 IOMMU + VFIO + 硬件支持 SR-IOV,配置陷阱多——错一步要么性能崩、要么宿主 panic。

适用 vs 不适用场景

适用

  • Linux 宿主机上跑大量 VM(公有云 IaaS / 私有云 / 桌面虚拟化)
  • 需要”VM 里跑任意 OS”的强隔离场景(不像容器只能跑 Linux)
  • 需要复用 Linux 已有的 cgroups / namespaces / 内存管理基础设施
  • 想用 KVM 做轻量化二次开发(Firecracker / Cloud Hypervisor / crosvm 都是这条路)

不适用

  • 宿主机不是 Linux(macOS 用 Hypervisor.framework / Windows 用 Hyper-V)
  • 没有 VT-x / AMD-V 的旧 CPU(KVM 不做软件全虚拟化)
  • 需要”hypervisor 比 OS 更安全”的高保障场景——Type-1(Xen / ESX)攻击面更小
  • 极致轻量场景容器够用(Docker / runc + namespaces)

历史小故事(可跳过)

  • 1974 年:Popek 和 Goldberg 发表《虚拟化三条件》论文——一种架构能被虚拟化必须满足”等价性 / 资源控制 / 效率”。x86 长期不满足(有 17 条”敏感但非特权”指令)。
  • 1999 年:VMware 发布桌面产品,靠二进制翻译绕开 x86 的虚拟化漏洞,证明了软件层也能做出可用 VMM。
  • 2003 年:剑桥团队发布 Xen,引入 para-virtualization——让 guest OS 改一下源码,主动配合 hypervisor,避开难虚拟化的指令。
  • 2005-2006 年:Intel VT-x / AMD-V 出货,硬件直接补上 1974 年那 17 条指令的缺口。
  • 2006 年秋:以色列工程师 Avi Kivity 在 Qumranet 公司启动 KVM,目标是”利用新硬件,做一个最小的 hypervisor”。
  • 2007 年 2 月:KVM 合入 Linux 2.6.20 主线(论文发表前就进了内核)。
  • 2007 年 6 月:本论文发于 OLS,整篇 6 页,作者就是 Avi Kivity 和 Qumranet 的同事们。
  • 2008 年:Red Hat 收购 Qumranet,KVM 成为 Red Hat 虚拟化战略核心。
  • 此后:OpenStack / oVirt / Proxmox / 各大公有云陆续把 KVM 当默认 hypervisor。

学到什么

  1. 复用现有系统比重写更聪明——Linux 的调度器 / 内存管理 / 隔离 / cgroups 已经被千万人调过参,hypervisor 借用就行
  2. 硬件能力一旦到位,软件设计可以骤然简化——VT-x 之前 hypervisor 是个大工程;之后 6 页论文就讲清楚
  3. Type-1 vs Type-2 不是非此即彼——KVM 模糊了边界,让”内核同时也是 hypervisor”成立
  4. 小内核 + 用户态扩展这个模式反复出现:KVM/QEMU、microkernel/server、Firecracker/host
  5. 论文写于产品成型之后——KVM 是”先合入主线、再写论文”的典型,工程界这种节奏很常见
  6. 抽象的最佳粒度是”够用就好”——KVM 模块只暴露 ioctl 几件套,没在内核里塞设备模拟、网络栈、磁盘格式,把复杂度推给用户态
  7. 生态绑定胜过技术领先——Xen 早三年、技术更激进,但 KVM 因为”在 Linux 主线里”自动获得了所有发行版的免费分发,最后赢下大盘

延伸阅读

  • 论文 6 页:Kivity et al. OLS 2007(密度高但好读,建议配代码看)
  • KVM 源码入口:linux/virt/kvm/ + linux/arch/x86/kvm/(架构相关在 arch 下)
  • Firecracker 设计文档:Firecracker NSDI 2020(看 KVM 哲学的现代延伸)
  • 视频:KVM Forum 历年演讲(社区年会,能看到各家厂商的优化思路)
  • xen-2003 —— 同时代竞争对手,Type-1 + para-virt
  • firecracker —— 砍极简的 KVM 用户态

关联

  • xen-2003 —— KVM 同时代主要对手,思路相反:Xen 是”另起一栋楼”,KVM 是”现房改造”
  • firecracker —— AWS Lambda 底座,本质是”砍掉 QEMU 的 KVM 用户态”
  • linux-kernel —— KVM 是 Linux 的一个内核子系统,与调度器/MM/cgroups 深度耦合
  • qemu —— KVM 的事实标配用户态搭档,IO 设备模拟全靠它
  • popek-goldberg-1974 —— 虚拟化三条件,KVM 时代靠硬件 VT-x 才真正满足

反向链接

  • esx-memory-2002 —— ESX Memory 2002 — 让一台机器假装比自己更大的四个魔术
  • firecracker-2020 —— Firecracker 2020 — 给 serverless 量身定做的极简 microVM
  • haven-2014 —— Haven — 把整个应用装进 CPU 黑盒,让云服务商也看不见
  • mach-vm-1987 —— Mach VM — 把虚拟内存抽象成”对象”,与硬件解耦
  • soltesz-2007 —— Soltesz 2007 — 容器:比虚拟机轻一档的隔离方案
  • xen-2003 —— Xen 2003 — 让操作系统配合虚拟化,性能直接接近原生