新建会话
Ctrl
K
Kimi+
历史会话

QEMU虚拟机热迁移中CPU状态保存与恢复详解

详细介绍一下qemu虚拟机热迁移过程中cpu状态的保存与恢复
QEMU virtual machine live migration CPU context preservation
目前搜索结果已覆盖 QEMU 热迁移的整体流程、设备状态(含 CPU)保存/恢复的框架、VMState 机制以及 pre_save/post_load 等回调的作用
。然而,对于“CPU 状态”这一关键主题,仍缺少寄存器级、KVM 加速场景下 vCPU 上下文如何序列化/反序列化的深入说明。为了给出寄存器级细节,需要进一步检索。
QEMU KVM vCPU registers live migration save restore
在 QEMU 虚拟机热迁移(live-migration)过程中,CPU 状态的保存与恢复是确保目标端能够无缝接管工作负载的核心环节。下面按“保存阶段”和“恢复阶段”两条主线,结合源码级流程(x86/KVM 场景)进行详细说明。

一、CPU 状态保存阶段(源端)

  1. 触发时机
    当脏页率降到阈值后,QEMU 进入 migration_completion(),先通过 vm_stop() 暂停所有 vCPU,然后调用 cpu_synchronize_all_states() 把“正在运行的 CPU 现场”一次性同步到 QEMU 数据区。
  2. 内核→QEMU 的数据搬运(以 KVM 为例)
    每个 vCPU 都会执行:
    复制
    kvm_cpu_synchronize_state()
    ├─ kvm_arch_get_registers()     // 读通用寄存器
    │  ├─ kvm_getput_regs(cpu, 0)   // KVM_GET_REGS:RAX~R15, RIP, RFLAGS
    │  ├─ kvm_get_xsave()           // XSAVE 区域:XMM/AVX/MPX 等扩展状态
    │  ├─ kvm_get_sregs()           // 段寄存器、GDTR、IDTR、CRx、EFER
    │  ├─ kvm_get_msrs()            // 型号专用寄存器
    │  └─ kvm_get_apic()            // 本地 APIC 状态(含 TPR、IRR、ISR)
    ├─ kvm_get_vcpu_events()        // 异常/中断状态(NMI、IRQ、pending MTF)
    └─ kvm_get_mp_state()           // 多处理器状态(wait-for-SIPI、INIT)
    以上调用把硬件辅助虚拟化层维护的寄存器全部拷贝到 CPUState 及其子结构,实现“内核→QEMU”的序列化。
  3. 序列化到迁移流
    每个设备(vCPU 也被视为一个设备)在 savevm_state_handlers 链表里注册了自己的 VMStateDescription;
    vmstate_save() 会遍历字段列表,用 VMSTATE_* 宏把 CPUState 中的成员按大端格式写入 QEMUFile(TCP/UNIX/RDMA 通道)。
    对于 x86,核心字段包括:
    复制
    VMSTATE_UINTTL(env.eip, CPUState),
    VMSTATE_UINTTL(env.regs[RAX], CPUState),
    ...
    VMSTATE_STRUCT(env.sregs, CPUState, 0, vmstate_sregs, SRegState),
    VMSTATE_UINT32(env.apic_base, CPUState),
    VMSTATE_STRUCT(env.apic_state, CPUState, 0, vmstate_apic, APICState),
    至此,CPU 完整快照已随迁移流发往目标端。

二、CPU 状态恢复阶段(目标端)

  1. 加载顺序
    目标端 QEMU 先按相同命令行创建空壳虚拟机,vCPU 处于 暂停态;
    迁移线程收到流后,依次执行:
    复制
    vmstate_load()                 // 反序列化
    ├─ pre_load() 回调             // 空/占位
    ├─ 逐字段读取并写回 CPUState
    └─ post_load() 回调            // 关键修复逻辑
  2. QEMU→内核的数据搬运
    反序列化完成后,QEMU 调用:
    复制
    cpu_synchronize_state()        // 标记 vcpu_dirty = true
    kvm_cpu_synchronize_state()    // 把数据写回 KVM
    ├─ kvm_arch_put_registers()
    │  ├─ kvm_getput_regs(cpu, 1)  // KVM_SET_REGS
    │  ├─ kvm_set_xsave()          // KVM_SET_XSAVE
    │  ├─ kvm_set_sregs()          // KVM_SET_SREGS
    │  ├─ kvm_set_msrs()           // KVM_SET_MSRS
    │  └─ kvm_set_apic()           // KVM_SET_LAPIC
    ├─ kvm_vcpu_events()           // KVM_SET_VCPU_EVENTS
    └─ kvm_set_mp_state()          // KVM_SET_MP_STATE
    至此,vCPU 硬件上下文已还原,与源端停止时的寄存器值逐位一致。
  3. 时钟与中断补偿
    • kvmclock 设备在 post_load() 会重新计算“迁移停顿时间”,把 KVMClockState.clock 加上 downtime,使 Guest 不会看到时间倒流。
    • 如果源端有未决中断(IRR/ISR),它们已在 kvm_set_apic() 阶段注入,目标端第一次入队即可被 Guest 认领。
  4. 启动 vCPU
    所有设备状态加载完毕后,迁移线程把 VM 状态置为 RUNNING,目标端 vCPU 进入 kvm_vcpu_ioctl(KVM_RUN),Guest 操作系统继续执行,从暂停前的 RIP 取下一条指令,对用户态应用完全透明。

三、小结关键点

  1. 两级同步:
    “内核↔QEMU”由 kvm_get/put_* 完成;“QEMU↔迁移流”由 VMState 宏完成。
  2. 暂停窗口极小:
    真正停止 vCPU 只有“最后迭代”到 KVM_RUN 重启之间,通常 < 300 ms。
  3. 状态完整:
    通用寄存器、扩展寄存器、段/控制寄存器、APIC、pending 中断、时钟漂移均被记录并重放,二进制级等价。
通过以上机制,QEMU 实现了寄存器级精确的热迁移,保证虚拟机跨物理机无缝漂移。