前言

虚拟化技术发展了 20 多年,QEMU 作为标杆项目积累了数百万行 C 代码 —— 功能极致丰富,但代码体积、攻击面、内存安全 CVE 数量都让它在 Serverless、机密计算等新场景下显得「过重」。

在 QEMU 之外,三个轻量级 VMM 几乎同时崛起:AWS 的 FirecrackerCloud Hypervisor、Google 的 crosvm。它们都用 Rust 写、都跑在 KVM 上、都瞄准「启动更快、内存更小、代码更精简」—— 但一个尴尬的事实是:这三个 VMM 把同一份基础代码独立写了三遍。KVM ioctl 封装、Guest 内存管理、virtqueue 实现、内核加载、事件循环 …… 每个项目都从零造了一遍轮子。每一行重复代码,都是一个潜在的内存漏洞入口。

2019 年由 Intel、Google、AWS、阿里云等多家厂商联合发起的 rust-vmm 项目,正是为了终结这种局面。它不是一款完整的 VMM,而是一套高度解耦、可按需组装的 Rust 虚拟化基础组件库 —— 把上述「重复造的轮子」沉淀为标准化、独立发布的 crate,让每个 VMM 项目专注于自己的差异化特性。

rust-vmm 遵循「一个组件一个 crate」的设计原则,所有 crate 独立发布、按需依赖,从最底层的系统调用封装到上层的设备模拟,覆盖了虚拟化技术栈的全部核心环节。本文将按功能分层,系统梳理 rust-vmm 生态下的全部核心 crate 及其定位、能力、核心 API 与最小调用示例,并在最后串出一个端到端的最小 VMM 实现。

核实声明:本文所有事实(仓库归属、架构支持、trait 名称、crate 是否存在、API 签名)均经过对 rust-vmm 组织 GitHub 仓库与 crates.io 的实际比对,更新于 2026-06;示例 API 以 docs.rs latest 为准。近一两年 rust-vmm 推动了多次「workspace 化合并」,部分原本独立的仓库(如 kvm-bindingskvm-ioctlsvfio-user)已被归档并整合到 workspace 仓库中,下文会标注实际位置。

整体架构:分层解耦的虚拟化积木

rust-vmm 采用自底向上的分层设计,每一层只依赖下层能力,职责边界清晰:

graph TD
    A[工程化与参考实现
rust-vmm-ci / rust-vmm-container / vmm-reference] --> B B[启动与兼容层
linux-loader / vm-fdt / vm-superio / vm-superio-ser] --> C C[硬件直通层
vfio-bindings / vfio-ioctls / vfio-user] --> D D[VirtIO & vhost 半虚拟化层
virtio-bindings / virtio-queue / virtio-queue-ser / vhost / vhost-user-backend / vhost-device-*] --> E E[核心资源层
vm-memory / vm-allocator / vm-device] --> F F[Hypervisor 抽象层
kvm-bindings / kvm-ioctls / mshv-bindings / mshv-ioctls] --> G G[基础系统工具层
vmm-sys-util / event-manager]

基础系统工具层

这一层是整个 rust-vmm 生态的公共基座,将操作系统底层能力封装为安全、跨平台的 Rust 接口。

vmm-sys-util

所有 rust-vmm 组件最核心的公共依赖。它将 eventfdioctlepoll、文件描述符管理、tempfile、字节序处理、errno 错误类型、灵活数组成员(FAM)安全封装等系统级原语统一收敛在一个 crate 中,把 unsafe 收拢到最小范围。

核心 APIeventfd::EventFdioctl_*! 宏家族(ioctl_none! / ioctl_io! / ioctl_ior! / ioctl_iow! / ioctl_iowr!)、ioctl_with_val / ioctl_with_reftempfile::TempFileepollerrno::Errorfam::FamStruct

最小示例(来自源码 doctest):

1
2
3
4
5
6
7
8
9
10
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};

let evt = EventFd::new(EFD_NONBLOCK).unwrap();
evt.write(55).unwrap();
assert_eq!(evt.read().unwrap(), 55);

// 跨线程共享同一计数(dup fd)
let evt_clone = evt.try_clone().unwrap();
evt.write(923).unwrap();
assert_eq!(evt_clone.read().unwrap(), 923);

EventFd 在 rust-vmm 里被广泛用作中断注入信号、退出信号、跨线程唤醒的轻量通道。

event-manager

轻量级事件驱动框架,VMM 中 I/O 事件、中断事件的核心调度器。它采用「事件管理器 - 事件订阅者」模型,提供两种订阅 trait 适配不同借用语义场景:

  • EventSubscriber:要求 &self,适用于通过 Arc<T> 共享、内部用 Mutex 自管的场景
  • MutEventSubscriber:要求 &mut self,适用于经 Mutex<T>/RefCell<T> 包装后由 manager 独占借用的场景

核心 APIEventManager<T>EventSubscriber / MutEventSubscriberEventOpsEventsSubscriberIdSubscriberOps

最小示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use event_manager::{EventManager, EventOps, Events, MutEventSubscriber};
use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};

pub struct CounterSubscriber { event_fd: EventFd, counter: u64 }

impl MutEventSubscriber for CounterSubscriber {
fn init(&mut self, ops: &mut EventOps) {
ops.add(Events::new(&self.event_fd, EventSet::IN)).unwrap();
}
fn process(&mut self, events: Events, ops: &mut EventOps) {
match events.event_set() {
EventSet::IN => { self.counter += 1; let _ = self.event_fd.read(); }
EventSet::HANG_UP => { ops.remove(events).ok(); }
_ => {}
}
}
}

let mut mgr = EventManager::<CounterSubscriber>::new().unwrap();
let _id = mgr.add_subscriber(CounterSubscriber {
event_fd: EventFd::new(0).unwrap(), counter: 0,
});
let _ = mgr.run_with_timeout(100); // 进入 epoll_wait

init 中注册感兴趣的 fd,process 中处理就绪事件;VMM 在主循环里反复调用 EventManager::run

Hypervisor 抽象层

这一层直接对接操作系统内核的虚拟化模块,将底层 unsafe 的内核接口封装为类型安全的 Rust API。rust-vmm 同时支持 Linux KVM 与微软 MSHV 两大虚拟化平台。

KVM 系列:Linux 硬件虚拟化标准接口

自 2024 年起,原 rust-vmm/kvm-bindingsrust-vmm/kvm-ioctls 已归档合并至单仓库 https://github.com/rust-vmm/kvm(workspace),但 crates.io 上的两个 crate 名称与发布节奏不变。

kvm-bindings

通过 bindgen 自动生成的 Linux KVM API 原生 FFI 绑定,包含所有 KVM ioctl 命令、数据结构与常量定义,并为灵活数组成员(如 kvm_cpuid2kvm_msr_list)提供安全的 FAM 封装,是 Rust 代码与 KVM 内核交互的底层协议基础。

kvm-ioctls

构建在 kvm-bindings 之上的安全封装层,将原本 unsafe 的 ioctl 调用转化为类型安全的高级 API。

C vs Rust 风格对比 —— 同样的「打开 KVM、创建 VM、注册 Guest 内存」三步,C 与 Rust 的差异:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// C 语言:手动管理 fd、手填结构体、逐个检查返回值
int kvm_fd = open("/dev/kvm", O_RDWR);
if (kvm_fd < 0) { /* errno */ }

int vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);
if (vm_fd < 0) { /* errno */ }

struct kvm_userspace_memory_region mem = {
.slot = 0,
.guest_phys_addr = 0x100000,
.memory_size = 128 * 1024 * 1024,
.userspace_addr = (uintptr_t)mmap_addr,
};
if (ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &mem) < 0) { /* errno */ }
1
2
3
4
5
6
7
8
9
10
11
// Rust:fd 由所有权管理、Result 强制错误处理;unsafe 仅围在内存注册一处
let kvm = Kvm::new()?;
let vm = kvm.create_vm()?;
let region = kvm_userspace_memory_region {
slot: 0,
guest_phys_addr: 0x100000,
memory_size: 128 << 20,
userspace_addr: mmap_addr as u64,
flags: 0,
};
unsafe { vm.set_user_memory_region(region)?; } // unsafe 因为 userspace_addr 是裸指针

差异不在于「行数少了几行」,而在于:错误必须显式处理(? 编译期强制),文件描述符与所有权绑定(不会泄漏 / 重复关闭),类型由编译器检查(不会把 vm_fd 误传给 Kvm 系统级 ioctl)。unsafe 被收缩到唯一一处真正越过 Rust 安全边界的调用上 —— 这就是「最小 unsafe 表面积」原则。

核心 API

  • Kvm:系统级 ioctl 入口(newcreate_vmget_api_versioncheck_extension
  • VmFd:VM 级(set_user_memory_regioncreate_vcpuget_dirty_logcreate_irq_chipregister_irqfd
  • VcpuFd:vCPU 级(get_regs/set_regsget_sregs/set_sregsrun
  • VcpuExit:枚举 Hlt / IoIn / IoOut / MmioRead / MmioWrite / …

最小示例(节选自 kvm-ioctls 顶层 doctest,x86_64):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES};
use kvm_ioctls::{Kvm, VcpuExit};

// guest 在 0x1000 处运行:out %al,%dx; mov $0,(0x8000); hlt
let asm: &[u8] = &[0xba,0xf8,0x03, 0x00,0xd8, 0x04,b'0', 0xee, 0xec,
0xc6,0x06,0x00,0x80,0x00, 0x8a,0x16,0x00,0x80, 0xf4];
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let mem_size = 0x4000; let guest_addr = 0x1000;
let host = unsafe { libc::mmap(std::ptr::null_mut(), mem_size,
libc::PROT_READ|libc::PROT_WRITE,
libc::MAP_ANONYMOUS|libc::MAP_SHARED, -1, 0) as *mut u8 };
unsafe { std::slice::from_raw_parts_mut(host, mem_size)[..asm.len()].copy_from_slice(asm); }
let region = kvm_userspace_memory_region {
slot: 0, guest_phys_addr: guest_addr, memory_size: mem_size as u64,
userspace_addr: host as u64, flags: KVM_MEM_LOG_DIRTY_PAGES,
};
unsafe { vm.set_user_memory_region(region).unwrap(); }

let vcpu = vm.create_vcpu(0).unwrap();
let mut sregs = vcpu.get_sregs().unwrap();
sregs.cs.base = 0; sregs.cs.selector = 0; vcpu.set_sregs(&sregs).unwrap();
let mut regs = vcpu.get_regs().unwrap();
regs.rip = guest_addr; regs.rax = 2; regs.rbx = 3; regs.rflags = 2;
vcpu.set_regs(&regs).unwrap();

loop {
match vcpu.run().unwrap() {
VcpuExit::IoOut(addr, data) => println!("OUT {:#x} = {:#x}", addr, data[0]),
VcpuExit::Hlt => break,
e => panic!("{:?}", e),
}
}

经典的「五步走」流程:Kvm::newcreate_vmset_user_memory_regioncreate_vcpu 并 setup regs → vcpu.run() 循环匹配 VcpuExit

MSHV 系列:Windows 平台虚拟化支持

mshv-bindings / mshv-ioctls

对应微软 Hypervisor(MSHV)的 FFI 绑定与安全接口封装,让 rust-vmm 突破 Linux 限制,可适配 Azure 等 Windows 云环境。设计逻辑与 KVM 系列完全一致(Mshv / VmFd / VcpuFd),降低跨平台开发的心智负担。

虚拟机核心资源层

这一层定义了虚拟机的核心资源模型 —— 内存、地址空间、设备抽象,是所有虚拟设备与业务逻辑的运行载体。

vm-memory

虚拟机客户机内存管理的基石组件。它支持匿名内存、文件 - backed 内存的 mmap 映射,实现了客户机物理地址(GPA)到宿主机虚拟地址(HVA)的安全转换,提供脏页跟踪、原子读写、零拷贝数据传输、跨字节序访问等核心能力,是设备 DMA 模拟、虚拟机热迁移、内存快照的基础支撑。

核心 API

  • trait:GuestMemory(只读视图)、GuestMemoryRegionBytesread/write/read_obj/write_obj)、Address
  • 类型:GuestAddressGuestUsizeGuestMemoryMmap<B = ()>MmapRegionGuestRegionMmapGuestMemoryAtomic(多线程换页 / 快照)

最小示例(来自 README):

1
2
3
4
5
6
7
8
9
10
11
12
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};

let gm: GuestMemoryMmap<()> = GuestMemoryMmap::from_ranges(&[
(GuestAddress(0), 0x1000),
(GuestAddress(0x1000), 0x1000),
]).unwrap();

let sample = &[1u8, 2, 3, 4, 5];
assert_eq!(gm.write(sample, GuestAddress(0xffc)).unwrap(), 5); // 跨 region 边界写
let mut buf = [0u8; 5];
assert_eq!(gm.read(&mut buf, GuestAddress(0xffc)).unwrap(), 5);
assert_eq!(&buf, sample);

GuestMemoryMmap::from_ranges 在宿主匿名 mmap 出来的内存基础上构建 guest 物理地址空间;Bytes trait 让设备模型直接以 (addr, slice) 方式跨 region 读写。

vm-allocator

虚拟机系统资源分配器,负责 MMIO 地址空间、PIO 端口、中断号(GSI)、设备 ID 等硬件资源的分配与回收。它支持地址区间对齐校验、资源冲突检测,确保虚拟机的硬件资源布局符合架构规范。

核心 API

  • IdAllocator:整数资源分配器(如 GSI、KVM memslot id),用 BTreeSet 维护已释放 ID
  • AddressAllocator:区间资源分配器(基于 IntervalTree 的地址段)
  • AllocPolicy::{FirstMatch, LastMatch, ExactMatch}ConstraintRangeInclusive

最小示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use vm_allocator::{AddressAllocator, AllocPolicy, IdAllocator};

const MMIO_START: u64 = (1 << 32) - (768 << 20); // 32-bit gap
const MMIO_SIZE: u64 = 768 << 20;
const PAGE: u64 = 0x1000;

let mut ids = IdAllocator::new(0, 100).unwrap(); // 闭区间 [0, 100],共 101 个 ID
let mut mmio = AddressAllocator::new(MMIO_START, MMIO_SIZE).unwrap();

// 给一个新设备分配一个 ID + 一页对齐的 MMIO 区间
let id = ids.allocate_id().unwrap();
let range = mmio.allocate(PAGE, PAGE, AllocPolicy::FirstMatch).unwrap();
println!("dev#{id} mmio = [{:#x}, {:#x}]", range.start(), range.end());

// 释放
mmio.free(&range).unwrap();
ids.free_id(id).unwrap();

vm-device

虚拟设备的统一抽象框架。它定义了 PIO 与 MMIO 两条总线上的设备 trait,并提供 IoManager 作为按地址段路由 I/O 的总线分发器。所有虚拟设备均可基于这套抽象开发,无缝接入任意 VMM。

核心 API

  • 设备 trait(共 4 个):
    • PIO:DevicePio(不可变 self)、MutDevicePio(可变 self)
    • MMIO:DeviceMmio(不可变 self)、MutDeviceMmio(可变 self)
  • 总线 trait:PioManagerMmioManager
  • 具体管理器:IoManager
  • 地址类型:PioAddressPioRangeMmioAddressMmioRange

最小示例(来自 README):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use std::sync::{Arc, Mutex};
use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
use vm_device::device_manager::{IoManager, PioManager};
use vm_device::MutDevicePio;

struct LogDevice;
impl MutDevicePio for LogDevice {
fn pio_read(&mut self, base: PioAddress, off: PioAddressOffset, _data: &mut [u8]) {
println!("read base={:?} off={off}", base);
}
fn pio_write(&mut self, base: PioAddress, off: PioAddressOffset, data: &[u8]) {
println!("write base={:?} off={off} data={:?}", base, data);
}
}

let mut mgr = IoManager::new();
let range = PioRange::new(PioAddress(0), 10).unwrap();
mgr.register_pio(range, Arc::new(Mutex::new(LogDevice))).unwrap();
mgr.pio_write(PioAddress(0), &[b'o', b'k']).unwrap(); // 由 IoManager 路由到 LogDevice

VMM 在 VcpuExit::IoOut/MmioWrite 上调用 mgr.pio_write/mmio_write,Manager 按地址范围找到对应设备并转发。

VirtIO 生态层

VirtIO 是云计算领域的事实半虚拟化设备标准,rust-vmm 提供了从协议绑定到队列实现、再到设备框架的完整支持。

仓库结构virtio-bindingsvirtio-queuevirtio-queue-servirtio-blkvirtio-consolevirtio-devicevirtio-vsock 等 crate 同属一个仓库 https://github.com/rust-vmm/vm-virtio(workspace),并非各自独立仓库,且 crates.io没有名为 vm-virtio 的 crate,仓库名只是 workspace 容器。

virtio-bindings

VirtIO 规范的原生 FFI 绑定(来源是 Linux kernel 的 virtio uapi 头文件),定义了 VirtIO 设备的标准数据结构、特性位、队列协议常量,按内核版本提供不同 feature。是所有 VirtIO 相关组件的协议底座。

virtio-queue

VirtIO Split Virtqueue 的纯 Rust 完整实现,包含描述符表、可用环(avail ring)、使用环(used ring)的全部逻辑,并通过 Kani 进行了形式化证明。

Split Virtqueue 由三段共享内存组成,driver(Guest)与 device(VMM)分别担任生产者与消费者:

1
2
3
4
5
6
7
8
9
10
11
12
13
          ┌────────────────────── Guest 物理内存(共享) ──────────────────────┐
│ │
driver │ Descriptor Table Avail Ring Used Ring │ device
(Guest) │ ┌──┬──┬──┬──┬──┐ ┌──┬──┬──┐ ┌──┬──┬──┐ │ (VMM)
│ │ 0│ 1│ 2│ 3│..│ │ 0│ 1│ 2│ │ 0│ 1│ 2│ │
│ └──┴──┴──┴──┴──┘ └──┴──┴──┘ └──┴──┴──┘ │
│ ↑ 描述缓冲区 (addr/len) ↑ 待处理的 chain ↑ 已完成结果 │
│ │
写入 ───→│ 填描述符 + 推 chain 到 avail ────────→ iter() 取出 ─→ 处理 │←── 读取
通知 ───→│ ↓ │
│ 从 used ring 拿到完成结果 ←──── add_used │
└───────────────────────────────────────────────────────────────────┘
中断(IRQ / eventfd) ←──────────── needs_notification

数据流:driver 把缓冲区地址填进 Descriptor Table,把 chain 头索引推到 Avail Ring 并踢一脚(通常通过 PCI/MMIO 写);device 通过 iter() 从 Avail Ring 拉出 DescriptorChain,处理完后用 add_used 把结果放回 Used Ring,再按 needs_notification 决定是否注入中断。

virtio-queue 把这三块结构封装为线程安全的 Rust 对象,并把所有「描述符链解析、回绕、内存屏障」之类的细节正确性问题做掉了 —— 这部分代码经过 Firecracker / Cloud Hypervisor / crosvm 三大 VMM 的生产验证,远比每个项目各自实现可靠。

核心 API

  • Queue(单线程)、QueueSyncArc<Mutex<Queue>>
  • trait:QueueT / QueueOwnedT
  • DescriptorDescriptorChainDescriptorChainRwIterAvailIter
  • QueueState(save/restore 用)

最小示例(节选自 Queue doctest):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use virtio_queue::{Queue, QueueOwnedT, QueueT};
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};

let mem: GuestMemoryMmap<()> =
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();

let mut queue = Queue::new(1024).unwrap();
queue.set_size(8);
queue.set_desc_table_address(Some(0x1000), None);
queue.set_avail_ring_address(Some(0x2000), None);
queue.set_used_ring_address(Some(0x3000), None);
queue.set_event_idx(true);
queue.set_ready(true);
assert!(queue.is_valid(&mem));

// 模拟 driver 把一个 chain head 推到 avail ring,并把 idx 推到 3
mem.write_obj::<u16>(3, GuestAddress(0x2002)).unwrap();

loop {
queue.disable_notification(&mem).unwrap();
while let Some(chain) = queue.iter(&mem).unwrap().next() {
// 这里遍历 chain 的描述符(chain.into_iter() / readable / writable)
queue.add_used(&mem, chain.head_index(), 0x100).unwrap();
if queue.needs_notification(&mem).unwrap() { /* 注入中断 */ }
}
if !queue.enable_notification(&mem).unwrap() { break; }
}

标准的 split virtqueue 处理循环 —— 禁用通知 → 迭代 AvailIter 取出 DescriptorChain → 处理后 add_used → 按 needs_notification 决定是否触发中断。

virtio-queue-ser

virtio-queue 的版本感知序列化扩展,对 QueueState 进行序列化与反序列化。它是热迁移的关键 —— 虚拟机暂停后,可将所有 VirtIO 设备的队列状态持久化,目标主机恢复后重建队列状态,保证业务无感知迁移。

vhost 与 vhost-user 层

vhost 系列组件将 VirtIO 设备的数据面从 VMM 进程中剥离,分别下沉到内核态(vhost)、独立用户态进程(vhost-user)或硬件(vDPA),既提升性能,又通过进程隔离缩小攻击面。

vhost

vhost、vhost-user、vDPA 三种加速方案的统一抽象库。它屏蔽了内核态 vhost、用户态 vhost-user、硬件 vDPA 的实现差异,提供统一的 vring 操作、事件通知、特性协商接口。

vhost-user-backend

vhost-user 后端设备的通用开发框架,完整封装了 vhost-user 协议的控制面交互逻辑:握手、特性协商、队列配置、生命周期管理、中断注入。开发者只需实现设备的数据面处理逻辑。

核心 API

  • trait:VhostUserBackend / VhostUserBackendMutnum_queuesmax_queue_sizefeaturesset_event_idxupdate_memoryhandle_eventprocess_queue 等)
  • VhostUserDaemon<S, V, B>:服务端入口(new / start / wait / get_epoll_handlers
  • vring trait:VringT,三种实现 VringState / VringMutex / VringRwLock

最小示例(来自 README,关键骨架;下面的 Error::* 为读者自定义错误类型,需结合具体后端实现):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use vhost_user_backend::{VhostUserBackendMut, VringMutex, VringT};

impl VhostUserBackendMut for MyBackend {
fn process_queue(&mut self, vring: &VringMutex) -> Result<bool> {
let mem = self.mem.as_ref().ok_or(Error::NoMem)?.memory();
let mut state = vring.get_mut();
while let Some(avail) = state.get_queue_mut().iter()
.map_err(|_| Error::IterateQueue)?.next()
{
// 1) 解析 avail 的 readable/writable 描述符并完成 I/O
let head = avail.head_index();
// 2) 归还到 used ring
state.add_used(head, 0).ok();
// 3) 必要时触发中断
if !self.event_idx || state.needs_notification().unwrap_or(true) {
state.signal_used_queue().ok();
}
}
Ok(true)
}
// 还需实现 num_queues / max_queue_size / features / protocol_features /
// set_event_idx / update_memory / handle_event ...
}

// 入口:
// let mut daemon = VhostUserDaemon::new(name, backend, atomic_mem)?;
// daemon.start(Listener::new("/tmp/sock", true)?)?;
// daemon.wait()?;

vhost-device 系列

rust-vmm 社区维护的开箱即用 vhost-user 设备集合,每个设备是 workspace 内独立的二进制 crate(vhost-device-<name>)。截至 2026-06,仓库内官方设备清单(按字母序):

crate 用途 crates.io
vhost-device-can CAN 总线(车载 / 工控) https://crates.io/crates/vhost-device-can
vhost-device-console 串口 / 控制台 https://crates.io/crates/vhost-device-console
vhost-device-gpio GPIO https://crates.io/crates/vhost-device-gpio
vhost-device-gpu GPU(virtio-gpu) https://crates.io/crates/vhost-device-gpu
vhost-device-i2c I2C 总线 https://crates.io/crates/vhost-device-i2c
vhost-device-input 输入设备(键鼠等) https://crates.io/crates/vhost-device-input
vhost-device-rng 熵源 / 随机数 https://crates.io/crates/vhost-device-rng
vhost-device-scmi ARM SCMI 协议 https://crates.io/crates/vhost-device-scmi
vhost-device-scsi SCSI 块存储 https://crates.io/crates/vhost-device-scsi
vhost-device-sound 音频(virtio-snd) https://crates.io/crates/vhost-device-sound
vhost-device-spi SPI 总线 https://crates.io/crates/vhost-device-spi
vhost-device-vsock guest-host socket 通道 https://crates.io/crates/vhost-device-vsock
vhost-device-template 新设备开发模板
vhost-device-video(staging) 视频编解码

辨析vhost-device-blkvhost-device-net 并不存在于本仓库。块设备 / 网络设备的 vhost-user 实现一般由 VMM 自带(如 Cloud Hypervisor 的 vhost-user-block、SPDK 的 vhost-user-blk、DPDK 的 vhost-user-net)或者部署内核态 vhost-net。

VFIO 硬件直通层

对于需要极致性能的场景(如 GPU 计算、高速网卡),rust-vmm 提供了完整的 VFIO 硬件直通支持。

rust-vmm/vfio-user 已于 2025 年归档,源码迁移至 https://github.com/rust-vmm/vfio workspace;vfio-bindingsvfio-ioctlsvfio-user 三个 crate 现位于同一仓库。

vfio-bindings

Linux VFIO(Virtual Function I/O)子系统的 FFI 绑定,包含 VFIO 容器、设备组、物理设备的所有数据结构与 ioctl 命令定义。

vfio-ioctls

VFIO API 的安全 Rust 封装,支持 PCIe 设备的完整透传流程。

核心 API

  • VfioContainer:包装 /dev/vfio/vfio,提供 vfio_dma_map / vfio_dma_unmap
  • VfioDevice:单个透传设备(get_irq_infoenable_irqregion_read / region_writeget_region_info
  • VfioGroupVfioRegionVfioIrq、错误类型 VfioError

最小骨架(仓库未提供独立 examples,下面按公共 API 还原):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use std::path::Path;
use std::sync::Arc;
use vfio_ioctls::{VfioContainer, VfioDevice};

// 1) 打开容器(device_fd 通常来自 KVM VmFd 实现的 hypervisor handle)
let container = Arc::new(VfioContainer::new(Some(device_fd))?);

// 2) 通过 sysfs 路径打开一个 PCI 设备,例如 0000:01:00.0
let sysfs = Path::new("/sys/bus/pci/devices/0000:01:00.0");
let dev = VfioDevice::new(sysfs, container.clone())?;

// 3) 查询 BAR0 region 信息 / 读寄存器
let info = dev.get_region_info(0)?;
let mut buf = [0u8; 4];
dev.region_read(0, &mut buf, 0);
println!("BAR0 size={:#x} flags={:#x} val={:?}", info.size, info.flags, buf);

// 4) 把一段宿主内存映射到 IOVA,供设备 DMA
container.vfio_dma_map(/*iova=*/0x1_0000_0000, len, host_va)?;

vfio-user

vfio-user 协议(基于 Nutanix libvfio-user 规范)的纯 Rust 实现,同时提供 Client(VMM 侧)与 Server(设备侧)两种 struct,支持把物理 / 模拟设备以 socket 形式远程挂载到虚拟机,适合分布式虚拟化与设备池化。

关于 iommufd:截至 2026-06,rust-vmm 组织下并不存在 iommufd-bindingsiommufd-ioctls 这两个 crate。Linux 新一代 iommufd 子系统的能力目前通过 vfio-bindings / vfio-ioctls 在 VFIO 容器/IOMMUFD 兼容路径中暴露;如未来 rust-vmm 推出独立的 iommufd crate,会以 workspace 子 crate 形式发布在 vfio 仓库。本节不再假装存在这两个 crate。

启动与兼容层

这一层提供虚拟机启动所需的内核加载、硬件信息传递、legacy 设备兼容能力,是 Guest OS 正常引导的基础。

vm-superio / vm-superio-ser

经典超级 I/O 控制器的模拟实现,包含:

  • Serial<T, EV, W>16550A UART 串口(PC 与多数嵌入式平台通用)
  • Rtc<EV>ARM PL031 RTC(注意是 ARM 的 PL031,不是 PC 上的 CMOS RTC,也不是 i8042)
  • I8042Device:极简 i8042 PS/2 控制器,目前仅模拟 CPU Reset 命令用于关机通告

vm-superio-ser 提供配套的 *StateSer 序列化结构,支持热迁移时 legacy 设备状态的 save/restore。

最小示例(16550A UART)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::io::{Error, Result};
use std::ops::Deref;
use vm_superio::{Serial, Trigger};
use vmm_sys_util::eventfd::EventFd;

struct EventFdTrigger(EventFd);
impl Trigger for EventFdTrigger {
type E = Error;
fn trigger(&self) -> Result<()> { self.0.write(1) }
}
impl Deref for EventFdTrigger {
type Target = EventFd;
fn deref(&self) -> &EventFd { &self.0 }
}

let intr = EventFdTrigger(EventFd::new(libc::EFD_NONBLOCK).unwrap());
let mut serial = Serial::new(intr, Vec::<u8>::new()); // 输出可换 io::stdout()

serial.write(0, b'H').unwrap(); // 写 THR -> 立刻刷到 out
serial.write(0, b'i').unwrap();
let input = b"abc";
if serial.fifo_capacity() >= input.len() {
serial.enqueue_raw_bytes(input).unwrap(); // 把数据塞进 RX FIFO,触发 RDA 中断
}
let _ = serial.read(0); // 从 RBR 读

最小示例(PL031 RTC)

1
2
3
4
5
6
7
8
9
10
11
12
13
use std::time::{SystemTime, UNIX_EPOCH};
use vm_superio::Rtc;

const RTCDR: u16 = 0x0; // Data Register
const RTCLR: u16 = 0x8; // Load Register

let mut rtc = Rtc::new();
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32;
let mut data = now.to_le_bytes();
rtc.write(RTCLR, &data); // 设置当前时间
rtc.read(RTCLR, &mut data);
assert_eq!(now, u32::from_le_bytes(data));
rtc.read(RTCDR, &mut data); // 读出递增后的时钟值

linux-loader

Linux 内核加载器,按架构提供不同的具体实现:

Loader feature 适用范围
loader::elf::Elf elf x86_64:同时承担 Linux 64-bit boot protocol 与 PVH boot protocol,PVH 入口由 ELF Note 发现,编码在结果的 pvh_boot_cap 字段
loader::bzimage::BzImage bzimage x86_64:传统 bzImage
loader::pe::PE pe aarch64 / riscv64:内核 Image(PE/COFF 格式)

注意:PVH 不是独立 loader,而是 Elf loader 通过 KernelLoaderResult::pvh_boot_cap 暴露的能力。

核心 API:trait KernelLoader::load、返回值 KernelLoaderResultkernel_loadkernel_endsetup_headerpvh_boot_cap)、独立函数 load_cmdline、命令行构造器 Cmdline

最小示例(节选自 README,bzImage):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::fs::File;
use linux_loader::loader::bzimage::BzImage; // 也可换成 elf::Elf / pe::PE
use linux_loader::loader::{KernelLoader, load_cmdline, Cmdline};
use vm_memory::{GuestAddress, GuestMemoryMmap};

let gm: GuestMemoryMmap<()> =
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 256 << 20)]).unwrap();
let mut kernel = File::open("bzImage").unwrap();

let res = BzImage::load::<File, GuestMemoryMmap<()>>(
&gm,
Some(GuestAddress(0x10_0000)), // kernel_offset
&mut kernel,
Some(GuestAddress(0x10_0000)), // himem_start
).expect("load bzImage");

let mut cl = Cmdline::new(0x1_0000).unwrap();
cl.insert_str("console=ttyS0 reboot=k panic=1").unwrap();
load_cmdline(&gm, GuestAddress(0x2_0000), &cl).unwrap();
println!("kernel loaded at {:#x}, end {:#x}", res.kernel_load.0, res.kernel_end);

vm-fdt

扁平化设备树(FDT/DTB)生成库,符合 Devicetree Specification 规范。在 aarch64、riscv64 等架构下,它负责向 Guest OS 传递硬件拓扑、内存布局、中断控制器、设备地址等启动信息。

核心 APIFdtWriternew / begin_node / end_node / finish)、节点句柄 FdtWriterNode、属性 setter(property_string / property_u32 / property_u64 / property_array_u32 / property_array_u64 / property_null / property_string_list / 裸字节 property)。

最小示例(来自 README):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use vm_fdt::{Error, FdtWriter};

fn create_fdt() -> Result<Vec<u8>, Error> {
let mut fdt = FdtWriter::new()?;

let root = fdt.begin_node("")?;
fdt.property_string("compatible", "linux,dummy-virt")?;
fdt.property_u32("#address-cells", 0x2)?;
fdt.property_u32("#size-cells", 0x2)?;

let chosen = fdt.begin_node("chosen")?;
fdt.property_u32("linux,pci-probe-only", 1)?;
fdt.property_string("bootargs", "panic=-1 console=hvc0")?;
fdt.end_node(chosen)?;

let memory = fdt.begin_node("memory")?;
fdt.property_string("device_type", "memory")?;
fdt.end_node(memory)?;

fdt.end_node(root)?;
fdt.finish() // -> Vec<u8>,可直接写入 guest 物理内存
}

工程化与参考实现

本节的三个项目都不是 crate(crates.io 上未发布),而是配套基础设施仓库。

rust-vmm-ci

社区统一的 CI 测试框架(Buildkite pipeline + pytest),通过 git submodule 引入到各 rust-vmm 仓库,提供一致的单元测试、格式检查(rustfmt)、Clippy、依赖漏洞审计、覆盖率检查。

rust-vmm-container

预配置的开发测试 Docker 镜像(rustvmm/dev),内置 Rust 工具链、x86_64 / aarch64 / riscv64 平台的内核头文件、QEMU 测试工具、依赖库等,让开发者一键搭建一致的 rust-vmm 开发与测试环境。

vmm-reference

官方参考 VMM 实现,通过极少量胶水代码串联起核心 rust-vmm 组件,构建出一个可直接运行的最小功能虚拟机。注意它声明为「实验性」、不应用于生产,定位是学习 / 验证 / 二次开发的范本。

端到端实战:用 rust-vmm 串一个最小 VMM

理解了各 crate 之后,我们用一段精简代码把 kvm-ioctls + vm-memory + linux-loader + event-manager + vm-device 串起来,看一个最小 VMM 在控制流上是什么样子。

声明:以下是概念性骨架,目的在于展示 API 拼装方式与控制流,省略了真正启动 Linux 所需的若干细节(boot params zero-page、CPUID/MSR、IDT、页表、APIC/GIC、ACPI/FDT、MPTABLE 等架构相关初始化)。要跑起一个真实 Linux Guest,请直接参考 vmm-reference

整体架构

一个生产级 VMM 通常由 API 线程 + 多个 vCPU 线程 + 一个事件循环线程 组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────────────────────────────┐
│ 一个 VMM 进程 │
│ │
│ struct Vmm { │
│ kvm: Kvm, // kvm-ioctls │
│ vm: VmFd, // kvm-ioctls │
│ vcpus: Vec<JoinHandle<...>>, // 每个 vCPU 一个线程 │
│ guest_mem: GuestMemoryAtomic, // vm-memory │
│ io_manager: IoManager, // vm-device │
│ event_manager: EventManager<...>, // event-manager │
│ } │
│ │
│ ┌──────────┐ ┌────────────────┐ ┌────────────────────┐ │
│ │ API 线程 │ │ vCPU 0 线程 │ │ 事件循环线程 │ │
│ │ 配置 VM │ │ loop: │ │ loop: │ │
│ │ 接受请求 │ │ vcpu.run() │ │ epoll_wait() │ │
│ └──────────┘ │ match Exit: │ │ → virtqueue 处理│ │
│ │ IoOut/Mmio │ │ → 网络/磁盘/串口 │ │
│ │ → io_mgr │ │ → 注入中断 │ │
│ │ Hlt → exit │ │ │ │
│ └────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

最小骨架代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES};
use kvm_ioctls::{Kvm, VcpuExit};
use linux_loader::loader::{elf::Elf, KernelLoader};
use std::fs::File;
use vm_memory::{Address, GuestAddress, GuestMemoryMmap};

const MEM_SIZE: usize = 128 << 20; // 128 MiB
const MEM_BASE: u64 = 0x10_0000; // Guest 物理 1 MiB 起始

fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1) 打开 KVM、创建 VM
let kvm = Kvm::new()?;
let vm = kvm.create_vm()?;

// 2) 分配 Guest 物理内存(vm-memory 抽象出 GPA→HVA 映射)
let guest_mem = GuestMemoryMmap::<()>::from_ranges(&[
(GuestAddress(MEM_BASE), MEM_SIZE),
])?;

// 3) 把每个 region 注册给 KVM —— 这是少数必须 unsafe 的地方,
// 因为我们把宿主裸指针交给了内核。
for (slot, region) in guest_mem.iter().enumerate() {
let mr = kvm_userspace_memory_region {
slot: slot as u32,
guest_phys_addr: region.start_addr().raw_value(),
memory_size: region.len() as u64,
userspace_addr: region.as_ptr() as u64,
flags: KVM_MEM_LOG_DIRTY_PAGES,
};
unsafe { vm.set_user_memory_region(mr)?; }
}

// 4) 加载内核(这里用 ELF;bzImage 换成 BzImage::load 即可)
let mut kfile = File::open("vmlinux")?;
let result = Elf::load::<File, GuestMemoryMmap<()>>(
&guest_mem,
None, // kernel_offset
&mut kfile,
Some(GuestAddress(MEM_BASE)), // himem_start
)?;

// 5) 创建 vCPU 并设置入口(真实场景还需 CPUID / MSR / sregs / IDT 等)
let vcpu = vm.create_vcpu(0)?;
let mut regs = vcpu.get_regs()?;
regs.rip = result.kernel_load.raw_value();
regs.rflags = 2; // x86 保留位
vcpu.set_regs(&regs)?;

// 6) VM-Exit 主循环:把 IO / MMIO 分发给 IoManager(这里只 println)
loop {
match vcpu.run()? {
VcpuExit::IoOut(port, data) => {
println!("[IoOut] port={:#x} data={:?}", port, data);
}
VcpuExit::IoIn(port, _) => {
println!("[IoIn] port={:#x}", port);
}
VcpuExit::MmioWrite(addr, data) => {
println!("[MmioWr] addr={:#x} data={:?}", addr, data);
}
VcpuExit::MmioRead(addr, _) => {
println!("[MmioRd] addr={:#x}", addr);
}
VcpuExit::Hlt => break,
other => println!("[exit] {:?}", other),
}
}
Ok(())
}

回顾这段代码的关键节点:

步骤 涉及 crate 干了什么
1 kvm-ioctls 打开 /dev/kvmKVM_CREATE_VM,得到 VM 句柄
2 vm-memory 在宿主匿名 mmap 上构建 GPA 到 HVA 的安全映射
3 kvm-ioctls + kvm-bindings KVM_SET_USER_MEMORY_REGION 把宿主内存注册为 memslot
4 linux-loader 解析内核格式、写入 Guest 内存、返回入口地址
5 kvm-ioctls 创建 vCPU 并设置寄存器(这里大幅简化)
6 kvm-ioctls + vm-device vcpu.run() 进入 KVM;VM-Exit 上分派给 IoManager

真实 VMM 还要做什么:在第 6 步之外,还有一个独立的 event-manager 线程跑 epoll,处理 virtio 设备的 eventfd notification、串口输入、API 请求 → 把消息塞进对应设备的 process_queue,再通过 irqfd 注入中断给 vCPU。这个事件循环 + vCPU 线程 + IoManager 三者协作的图景,就是上面架构图里展示的内容。

如果要看这段骨架完整跑起来的版本,最好的范本就是 vmm-reference:它在不到 5 千行 Rust 代码里串联了几乎所有 rust-vmm 组件,是「活文档」级别的学习材料。

生态落地:三大 Rust VMM 一览

rust-vmm 的价值最终体现在它被多个生产级 VMM 实际复用上。下面看三大 VMM 各自的差异化策略。

Firecracker(AWS)

Firecracker 是 rust-vmm 最早的采用者之一,定位 Serverless/FaaS,设计哲学是「做减法」:

  • 只支持 KVM、x86_64 / aarch64
  • 仅实现 Serverless 场景必需的设备(VirtIO-net / VirtIO-blk / Serial / vsock 等),无 PCI 总线(仅使用 VirtIO-MMIO)
  • 自带 Jailer 安全沙箱:pivot_root 文件系统隔离 + seccomp 三线程独立 filter + cgroups 资源限制 + setuid 降权
  • 支持微快照与按需分页(基于 userfaultfd),冷启动可降至毫秒级

主要复用的 rust-vmm crate:kvm-ioctlsvm-memoryvmm-sys-utilvirtio-queuelinux-loadervm-superioevent-manager

Cloud Hypervisor(Linux Foundation)

Cloud Hypervisor 最初由 Intel 发起,现由 Linux Foundation 社区维护,定位 通用云虚拟机,设计哲学是「做加法」:

  • 同时支持 KVM 与 MSHV(通过其内置的 hypervisor 抽象 crate 切换;该 crate 属于 Cloud Hypervisor 项目,不在 rust-vmm 组织下
  • 使用 VirtIO-PCI 而非 VirtIO-MMIO,配合 ACPI 设备发现,对 Windows Guest 友好
  • 设备模型丰富:virtio-gpu、virtio-fs、vhost-user 后端、热插拔
  • 支持机密计算(Intel TDX、AMD SEV/SEV-SNP)、VFIO 直通

几乎覆盖 rust-vmm 全部组件,是 Kata Containers 的主流后端之一。

crosvm(Google)

crosvm 是 Google 为 ChromeOS、Android 打造的 VMM,也是 rust-vmm 多个组件的重要上游贡献者。它的杀手锏是 每设备独立沙箱进程

1
2
3
4
5
6
7
8
9
10
crosvm 主进程(最小权限)
├── virtio-blk 设备 → 独立子进程(只能访问磁盘镜像)
├── virtio-net 设备 → 独立子进程(只能访问 tap fd)
├── virtio-gpu 设备 → 独立子进程(只能访问 GPU)
└── virtio-snd 设备 → 独立子进程(只能访问音频接口)

每个设备进程都有:
- 独立的 mount/pid/net namespace
- seccomp-bpf 限制系统调用白名单
- 通过 Unix socket 与主进程通信

ChromeOS 的运行环境天然是不可信用户(浏览器)—— 即使某个设备模拟存在漏洞被攻破,攻击者也只能拿到一个权限极度受限的子进程。这种「VMM 内部纵深防御」比 Firecracker 的 Jailer 更进一步。

三大 VMM 对比

维度 Firecracker Cloud Hypervisor crosvm
背后 AWS Linux Foundation Google
哲学 极简 完备 纵深隔离
设备总线 VirtIO-MMIO VirtIO-PCI VirtIO-MMIO + PCI
Hypervisor KVM KVM + MSHV KVM
Guest OS Linux Linux + Windows Linux + Android
沙箱模型 Jailer 多层防御 无内置(部署侧自行沙箱) 每设备独立进程
杀手锏 微快照 + CPU Template 热插拔 + 机密计算 设备级沙箱
典型场景 Serverless / FaaS 通用云 / Kata ChromeOS / Android

国内生态中,Dragonball(蚂蚁/阿里、Kata 内置 VMM)与 StratoVirt(华为 openEuler,覆盖云、边缘、嵌入式)也都基于 rust-vmm 构建。

为什么不统一成一个 VMM?

读完上面的对比,自然会问:既然都用 rust-vmm,为什么不直接维护一个统一的 VMM?

答案是 —— 不同场景对 VMM 的需求天然冲突

  • AWS Lambda:极致冷启动(毫秒级)、最小内存开销(几 MB)、只跑 Linux、不需要 PCI / GPU / USB → 要的是「减法
  • Cloud Hypervisor:支持 Windows、PCI 设备、热插拔、多 Hypervisor 后端、接近通用虚拟机的功能完备性 → 要的是「加法
  • crosvm:每个设备独立沙箱进程、ChromeOS / Android 深度集成、virtio-gpu 图形加速 → 要的是「隔离

减法、加法、隔离 —— 这三者在架构层面就是矛盾的。试图用一个 VMM 满足所有场景,结果必然是代码膨胀、性能妥协、安全模型妥协。rust-vmm 的设计智慧正在于此:共享基础设施(KVM 封装、内存管理、virtqueue),但不共享产品本身。每个 VMM 在自己的场景里做到极致,同时从共享组件中受益。

安全:Rust 改变游戏规则

如果说 rust-vmm 解决了「重复造轮子」的效率问题,那么 Rust 语言本身就解决了虚拟化领域最大的痛点 —— 内存安全

Rust 编译期消灭的几类漏洞

QEMU 历史上的内存安全 CVE 几乎全部是 C 语言的固有顽疾:

漏洞类别(CWE) C 中的常见成因 Rust 编译期保证
缓冲区溢出(CWE-119/787) 手动越界写 数组/切片下标编译期或运行时检查
空指针解引用(CWE-476) NULL 检查遗漏 Option<T> 强制模式匹配,引用永远非空
Use-After-Free(CWE-416) 释放后继续访问 借用检查器 + 生命周期保证
Double Free(CWE-415) 重复 free 所有权移动,单一所有者 drop
数据竞争(CWE-362) 多线程裸共享 Send / Sync trait 编译期约束

Guest VM 本身就是潜在的恶意环境 —— 它能通过 MMIO / PIO / virtqueue 触发 VMM 中的任何代码路径。用 C 写 VMM 像在不设防的城市里巡逻;用 Rust 写至少城墙是坚固的。

不是「零 CVE」,是「显著降维」

⚠️ 请避免被「Rust VMM 零 CVE」的说法误导。 Firecracker / Cloud Hypervisor / crosvm 历史上都披露过 CVE,只是相比 QEMU 的 CWE-119 系列内存破坏类漏洞,Rust VMM 中典型 C 类内存安全问题显著减少。仍然存在的风险包括:

  • unsafe 块中的 bug:FFI、裸指针操作(如 set_user_memory_region)依然可能引入 UAF / 越界,需要额外审计
  • 逻辑漏洞:权限校验缺失、状态机错误、整数溢出语义错误 —— 这些 Rust 不管
  • 侧信道 / DoS:如 vCPU 资源耗尽、虚假中断风暴
  • 依赖链供应链攻击:Cargo.lock 锁版本可降低但不能消除

真正的安全收益是「攻击面降维」—— 把绝大多数低成本、易批量化的内存破坏 bug 在编译期消灭,让审计与 fuzzing 可以聚焦在更窄的 unsafe 表面与逻辑层面。

rust-vmm 的额外安全实践

  • 每个 crate 经过社区与安全专家共同的代码审计
  • 共享 CI(rust-vmm-ci)统一跑 Clippy、cargo audit、fuzzing
  • 关键组件(如 virtio-queue)用 Kani 做形式化证明
  • unsafe 块通常被收拢到 vmm-sys-util*-bindings*-ioctls 等少数 crate 中,便于集中审计

总结

回顾 VMM 的二十年演进:

1
2
3
4
5
6
7
8
2000s  ──  QEMU
└─ 单体 C 代码、百万行级别、CWE-119 系列 CVE 频发

2010s ── Firecracker / crosvm / Cloud Hypervisor
└─ 轻量级 + Rust 安全模型,但三家各自造一遍 KVM/virtio/loader

2020s ── rust-vmm
└─ 组件化共享底座 + Rust 内存安全,厂商专注差异化竞争

rust-vmm 最深刻的意义不在某个 crate 写得多好,而在于它改变了虚拟化领域的协作模式 —— AWS、Intel、Google 这三家在云市场上短兵相接的对手,选择在 VMM 基础设施层面携手合作。这不是慈善,而是工程理性的胜利:当底层代码的安全性与正确性可以被共享验证,每一个参与者都受益。

「基础共享、上层竞争」的模式正在产生实实在在的成果:

  • Firecracker 让 AWS Lambda 的冷启动从秒级降至毫秒级
  • Cloud Hypervisor 让 Windows VM 在 KVM/MSHV 上同样高效,并打通 TDX/SEV 机密计算路径
  • crosvm 把 ChromeOS 的安全模型从浏览器延伸到了虚拟机
  • Dragonball / StratoVirt 让国内云厂商在容器与边缘场景拿到自主可控的轻量 VMM

随着云原生与安全需求的持续升级,rust-vmm 生态仍在不断扩展 —— RISC-V 架构(KVM / linux-loader / vm-memory 已稳定支持)、机密计算(TDX / SEV / SEV-SNP / pKVM)、更多设备模型(sound、video、scmi)都在持续推进。它不仅是 Rust 语言在系统级领域的标杆项目,更是下一代云基础设施的重要技术基石。

虚拟化的下一个十年,属于 Rust。

参考链接