本页目录

QEMU 与 KVM 协作

覆盖: QEMU-KVM 进程模型 → KVM_CREATE_VM/KVM_RUN → 内存布局 → vCPU 线程循环 → 设备模拟 → live migration 内核版本: 2.6.20 ~ 6.x

进程模型

flowchart TD
    QEMU["一个 VM = 一个 QEMU 进程"]

    QEMU --> MAIN["🧵 Main 线程<br/>event loop, monitor"]
    QEMU --> IO["🧵 IO 线程 (iothread)<br/>处理 virtio 设备 IO"]
    QEMU --> VCPU["🧵 vCPU 线程 × N<br/>每个执行 KVM_RUN"]
    QEMU --> MIGRATE["🧵 迁移线程<br/>live migration"]

    classDef root fill:#e3f2fd,stroke:#1565c0
    classDef thread fill:#fff3e0,stroke:#ef6c00
    class QEMU root
    class MAIN,IO,VCPU,MIGRATE thread

VM 创建流程

// QEMU → KVM:
kvm_fd = open("/dev/kvm", O_RDWR);

// 1. 创建 VM
vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);

// 2. 分配 guest RAM (QEMU 管理):
guest_ram = mmap(NULL, ram_size, PROT_READ|PROT_WRITE,
                 MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);

// 3. 注册到 KVM:
struct kvm_userspace_memory_region region = {
    .slot = 0,              // memory slot ID
    .guest_phys_addr = 0,   // guest 物理地址起始
    .memory_size = ram_size,
    .userspace_addr = (uintptr_t)guest_ram,
};
ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &region);

// 4. 创建 vCPU:
for (int i = 0; i < num_vcpus; i++) {
    vcpu_fd[i] = ioctl(vm_fd, KVM_CREATE_VCPU, i);
    mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
    vcpu->kvm_run = mmap(NULL, mmap_size, ..., vcpu_fd[i], 0);
    // kvm_run 是共享内存: KVM 在这里写 exit_reason 和寄存器状态
}

vCPU 运行循环

// QEMU: kvm_cpu_exec() (accel/kvm/kvm-all.c)
int kvm_cpu_exec(CPUState *cpu) {
    struct kvm_run *run = cpu->kvm_run;
    int ret;

    do {
        // 1. 注入待处理的中断 (如果有)
        if (cpu->interrupt_request)
            kvm_arch_put_registers(cpu);

        // 2. 进入 guest!
        ret = ioctl(cpu->kvm_fd, KVM_RUN, 0);

        // 3. VM exit → 处理:
        switch (run->exit_reason) {
        case KVM_EXIT_IO:
            kvm_handle_io(run->io.port, run->io.direction, run->io.size, run->io.count,
                          run->io.data_offset);
            break;
        case KVM_EXIT_MMIO:
            address_space_rw(run->mmio.phys_addr, ...);
            break;
        case KVM_EXIT_IRQ_WINDOW_OPEN:
            // 可以注入中断了
            break;
        case KVM_EXIT_SHUTDOWN:
            // guest 三重故障 → 停止
            return -1;
        }
    } while (ret == 0);

    return ret;
}

IO 线程与 virtio 处理

// QEMU iothread: 独立的 event loop
//   → epoll on eventfd (virtio kick)
//   → QEMU 处理 virtio 请求:
//       virtio-blk: read/write → 映射到 host 文件 (qcow2/raw)
//       virtio-net: tap fd → 读/写 Linux tun/tap
//
// vhost (绕过 QEMU):
//   ioeventfd + irqfd → 内核直接处理
//   → QEMU 只需配置, 不参与数据路径

Live Migration

QEMU + KVM 协作:
  1. 源端: 标记所有 guest RAM 为 read-only
     → 后续写产生 write fault → 记录脏页
  2. 迭代复制: RAM 预拷贝到目标
  3. 脏页收敛: 每轮 dirty rate ↓ → 到阈值
  4. stop-and-copy: 暂停源 VM → 复制最后脏页 + 寄存器状态 → 目标启动
  5. downtime ~100ms (取决于工作负载)

参考

  • 源码⁠: QEMU accel/kvm/kvm-all.c (KVM 加速核心), Linux virt/kvm/kvm_main.c
  • 内核文档⁠: Documentation/virt/kvm/api.rst (ioctl API 手册)

关键词: KVM_RUN, kvm_run, ioctl, KVM_CREATE_VM, vCPU thread, iothread, live migration