当前位置: 首页 > news >正文

libvirt/qemu内存快照的实现原理分析记录

libvirt 内存快照实现原理深度分析

1. 概述

当你执行virsh snapshot-create-as <domain> --memspec savefilevirsh snapshot-create <domain> snapshot.xml(其中 XML 指定了<memory snapshot='external'/>),libvirt 会创建一个包含虚拟机内存状态的快照。这看似简单的一条命令,背后却涉及 libvirt 多层架构的精密协作——从公共 API 到 QEMU 驱动,再到 QEMU Monitor 命令。本文将逐层拆解。

2. 快照类型与内存快照定位

libvirt 定义了三种内存快照位置(snapshot_conf.h):

typedefenum{VIR_DOMAIN_SNAPSHOT_NOSTATE=VIR_DOMAIN_NOSTATE,VIR_DOMAIN_SNAPSHOT_RUNNING=VIR_DOMAIN_RUNNING,VIR_DOMAIN_SNAPSHOT_PAUSED=VIR_DOMAIN_PAUSED,VIR_DOMAIN_SNAPSHOT_SHUTOFF=VIR_DOMAIN_SHUTOFF,// ...VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT,// 磁盘快照专用状态}virDomainSnapshotState;

快照定义的核心数据结构(snapshot_conf.h):

struct_virDomainSnapshotDef{virDomainMomentDef parent;intstate;// 快照时的域状态virDomainSnapshotLocation memory;// 内存快照类型:internal/external/nochar*memorysnapshotfile;// 外部内存状态文件路径size_tndisks;virDomainSnapshotDiskDef*disks;// ...};

关键字段解读:

  • memory:决定内存快照是internal(嵌入 qcow2 磁盘文件)、external(独立文件)还是no(不保存内存)
  • memorysnapshotfile:当 memory 为 external 时,保存内存状态的文件路径
  • state:快照时刻 VM 的状态,回滚时用于判断是否需要恢复内存

--memspec本质就是设置memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL,并指定memorysnapshotfile

3. API 入口与调用链

整体调用链如下:

virDomainSnapshotCreateXML() // 公共 API: src/libvirt-domain-snapshot.c └→ qemuDomainSnapshotCreateXML() // QEMU 驱动实现: src/qemu/qemu_driver.c └→ qemuSnapshotCreateXML() // 快照核心逻辑: src/qemu/qemu_snapshot.c

4. 准备阶段:qemuSnapshotPrepare

在真正执行快照前,qemuSnapshotPrepare()(qemu_snapshot.c)负责校验合法性。关键逻辑:

staticintqemuSnapshotPrepare(virDomainObj*vm,virDomainSnapshotDef*def,bool*has_manual,unsignedint*flags){bool active=virDomainObjIsActive(vm);// ...for(i=0;i<def->ndisks;i++){switch(disk->snapshot){caseVIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL:found_internal=true;// 活跃域不能使用内部磁盘快照 + 外部内存快照的组合break;caseVIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL:external++;break;// ...}}// 关键约束:内部内存快照要求所有磁盘都参与内部快照if((def->memory==VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL&&!found_internal)||(found_internal&&forbid_internal)){// 报错:内部快照要求所有磁盘都参与}// 不允许混合内部和外部if((def->memory==VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL&&found_internal)){// 报错:混合内部和外部目标不支持}}

核心约束

  1. 外部内存快照(--memspec)+ 外部磁盘快照:✅ 合法组合
  2. 外部内存快照 + 内部磁盘快照:❌ 不允许
  3. 内部内存快照 + 外部磁盘快照:❌ 不允许
  4. 内部内存快照要求所有磁盘都参与内部快照

5. 核心执行:qemuSnapshotCreateActiveExternal

这是内存快照的真正核心入口(qemu_snapshot.c):

staticintqemuSnapshotCreateActiveExternal(virQEMUDriver*driver,virDomainObj*vm,virDomainMomentObj*snap,virQEMUDriverConfig*cfg,bool has_manual,unsignedintflags){bool memory=snapdef->memory==VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;bool thaw=false;bool resume=false;// ...

整个流程可以分解为以下阶段:

5.1 文件系统冻结(Quiesce)

if(flags&VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE){frozen=qemuSnapshotFSFreeze(vm,NULL,0);if(frozen>0)thaw=true;// 标记后续需要解冻}

如果用户指定了--quiesce,libvirt 通过 QEMU Guest Agent 调用fsfreeze冻结客户机文件系统,确保磁盘数据一致性。

5.2 暂停虚拟机

if(virDomainObjGetState(vm,NULL)==VIR_DOMAIN_RUNNING){if(memory&&!has_manual)resume=true;// 标记后续需要恢复运行if(((memory||has_manual)&&!(flags&VIR_DOMAIN_SNAPSHOT_CREATE_LIVE))){if(qemuProcessStopCPUs(driver,vm,VIR_DOMAIN_PAUSED_SNAPSHOT,VIR_ASYNC_JOB_SNAPSHOT)<0)gotocleanup;}}

关键点

  • 内存快照必须暂停 VM(非 Live 模式下),因为需要获取一个一致的内存状态
  • resume标记记录了 VM 原本是运行状态,快照完成后需要恢复
  • VIR_DOMAIN_SNAPSHOT_CREATE_LIVE标志允许不暂停 VM(但这是渐进式,更复杂)

5.3 创建外部磁盘快照

if(qemuSnapshotCreateActiveExternalDisks(vm,snap,blockNamedNodeData,flags,asyncJob)<0)gotocleanup;

外部磁盘快照通过 QMPtransaction命令原子性地完成:

  • 为每个磁盘创建 qcow2 overlay 文件
  • 使用blockdev-snapshot-syncblockdev-snapshot命令
  • 原始磁盘变为只读 backing file,新 overlay 成为活跃层

5.4 保存内存状态

这是内存快照最关键的步骤:

if(memory){// 将 VM 状态保存到 memorysnapshotfile 指定的文件if(qemuSaveImageCreate(driver,vm,snapdef->memorysnapshotfile,compressor,asyncJob)<0)gotocleanup;memory_unlink=true;}

qemuSaveImageCreate内部调用 QEMU Monitor 的migrate命令,将内存页转储到文件:

QMP: {"execute": "migrate", "arguments": {"uri": "exec:cat > /path/to/savefile"}}

本质上,这个过程和virsh save一样——QEMU 的 live migration 机制被复用来将内存页写入文件。保存的数据包含:

  1. libvirt save header:包含 XML 域定义、cookie 等
  2. QEMU vmstate:设备状态
  3. 内存页数据:客户机物理内存的完整转储

5.5 恢复虚拟机运行

cleanup:if(thaw)qemuSnapshotFSThaw(vm,false);// 解冻文件系统if(resume&&virDomainObjIsActive(vm))qemuProcessStartCPUs(driver,vm,VIR_DOMAIN_RUNNING_UNPAUSED,VIR_ASYNC_JOB_SNAPSHOT);// 恢复 CPU 运行

5.6 错误回滚

if(ret<0&&memory_unlink&&!memory_existing)unlink(snapdef->memorysnapshotfile);// 删除未完成的内存转储文件

如果过程中任何步骤失败,已创建的内存文件和磁盘 overlay 都会被回滚清理。

6. 内部内存快照路径

对于内部内存快照(memory = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL),走的是完全不同的路径(qemu_snapshot.c):

staticintqemuSnapshotCreateActiveInternal(virQEMUDriver*driver,virDomainObj*vm,virDomainMomentObj*snap,unsignedintflags){// 1. 暂停 VMif(virDomainObjGetState(vm,NULL)==VIR_DOMAIN_RUNNING){qemuProcessStopCPUs(driver,vm,VIR_DOMAIN_PAUSED_SAVE,...);resume=true;}// 2. 通过 QMP 发送 snapshot-save 命令job=qemuSnapshotCreateActiveInternalStart(vm,snapdef);// 内部调用: qemuMonitorSnapshotSave()// 3. 等待异步 job 完成while((rv=qemuSnapshotCreateActiveInternalDone(vm,job))!=1){qemuDomainObjWait(vm);}}

内部快照使用 QMP 命令snapshot-save

rc=qemuMonitorSnapshotSave(qemuDomainGetMonitor(vm),jobname,snapdef->parent.name,devices[0],(constchar**)devices);

这个命令将 vmstate(内存 + 设备状态)嵌入到 qcow2 磁盘映像文件中。QEMU 的 qcow2 格式本身支持这种 vmstate 存储机制。

外部 vs 内部内存快照对比

维度外部(--memspec内部
内存存储位置独立文件qcow2 磁盘文件内部
磁盘快照类型必须 external必须 internal
QEMU 命令migrate到文件snapshot-save
磁盘格式要求无特殊必须 qcow2
文件系统影响额外产生一个文件qcow2 文件膨胀

7. 回滚(Revert)到内存快照

当执行virsh snapshot-revert回滚到内存快照时,流程如下(在qemuSnapshotRevertActive中):

  1. 如果目标快照包含内存状态memory != NOstate == PAUSED/RUNNING):

    • 如果 VM 正在运行,先停止 VM
    • 使用qemuProcessStart()重新启动 VM,并传入内存快照文件
    • qemuProcessStart内部通过 QEMU 的-incoming参数和migrate incoming恢复内存状态
    • 如果快照状态是PAUSED,恢复后保持暂停
  2. 如果目标快照没有内存状态state == SHUTOFF):

    • 停止 VM
    • 切换磁盘到快照对应的状态
    • VM 保持关闭

8. QEMU 层面:内存保存的底层机制

当 libvirt 调用qemuSaveImageCreate时,最终的 QEMU 执行路径:

libvirt: qemuSaveImageCreate() └→ qemuMonitorMigrateToFile() └→ QMP: {"execute": "migrate", "arguments": {"uri": "exec:..."}} └→ QEMU: migration/thread → RAM save → vmstate_save → 写入文件

QEMU 的 migration 框架被复用于内存转储:

  1. RAM save:遍历客户机物理内存页,逐页写入目标(通常经过压缩)
  2. vmstate save:保存所有虚拟设备的状态(CPU 寄存器、设备配置等)
  3. 数据流经过 libvirt 的 compressor 管道(可选压缩),最终写入memorysnapshotfile

9. 完整流程图

virsh snapshot-create-as --memspec savefile │ ▼ virDomainSnapshotCreateXML() ← 公共 API │ ▼ qemuDomainSnapshotCreateXML() ← QEMU 驱动 │ ▼ qemuSnapshotCreateXML() ← 解析 XML、创建 snapdef │ ▼ qemuSnapshotPrepare() ← 校验磁盘/内存快照组合合法性 │ ▼ qemuSnapshotCreateActiveExternal() │ ├── 1. FSFreeze (quiesce) ← QEMU Guest Agent ├── 2. StopCPUs ← 暂停 VM ├── 3. External Disk Snapshot ← QMP transaction (blockdev-snapshot) ├── 4. Save Memory State ← QMP migrate → memorysnapshotfile ├── 5. FSThaw ← 解冻文件系统 └── 6. StartCPUs ← 恢复 VM 运行

10. 总结

libvirt 的内存快照实现是一个精密的多层协作过程:

  1. 配置层snapshot_conf):定义快照元数据,区分memory字段的 internal/external/no
  2. 校验层qemuSnapshotPrepare):确保磁盘和内存快照类型的兼容性
  3. 执行层qemuSnapshotCreateActiveExternal):编排暂停、磁盘快照、内存转储、恢复的完整序列
  4. QEMU 层:通过 QMPtransaction完成磁盘快照,通过migrate完成内存转储

外部内存快照(--memspec)的设计哲学是解耦——内存状态和磁盘状态分别存储在独立文件中,不依赖 qcow2 的内部快照能力,这为存储后端的灵活性提供了基础。


http://www.jsqmd.com/news/885503/

相关文章:

  • 软件项目管理(5):AI 辅助开发下的审查与上线门禁
  • 20244321 2025-2026-2 《Python程序设计》实验四报告
  • 英文初稿查AI率、降ai率,这几个宝藏网站直接搞定! - 殷念写论文
  • TII投稿避坑实录:从LaTeX编译报错到作者照片命名,我踩过的那些雷
  • Nginx CORS配置陷阱:Origin反射与Credentials滥用风险解析
  • 3步快速上手:TigerVNC实现跨平台远程桌面控制的完整指南
  • FinceptTerminal 深度拆解:23k Star 的开源金融终端,到底做对了什么?
  • 我仓库内cad python 有哪些应用到聚类的方法
  • Bedrock Prompt Optimization 进阶版:5 个模型同时对比,一条 Prompt 自动调到能打
  • 超声波液位计厂家排行榜:2026年国产十大品牌深度评测与选型指南 - 仪表品牌榜
  • Simulink模型测试踩坑实录:Test Manager里那些容易忽略的配置项(比如Comparison勾选)
  • 用python进行简单计算
  • 系统单一时区场景下的时间类型传输设计方案(固定时区:东八区)
  • 决战破晓手游官网下载:决战破晓最新官方下载渠道
  • 基于Arduino的MPPT太阳能充电控制器:从Buck电路到算法实现全解析
  • Product Hunt 每日热榜 | 2026-05-24
  • Recuva真的能恢复被‘文件粉碎’的数据吗?实测腾讯管家、火绒删除后的恢复效果
  • WPF控件颜色集合
  • 我用DMXAPI同时调用DeepSeek和Kimi,做了一个能处理长文档的问答工具
  • 牛客周赛Round145
  • taotoken token plan套餐在实际开发中的成本节省感受
  • 主流源代码管理工具介绍
  • 如何在Windows 11上免费安装安卓子系统:完整简易指南
  • 为学术研究项目构建可复现且成本可控的大模型实验平台
  • NS-USBLoader终极指南:一站式Switch文件传输与RCM注入解决方案
  • 从XP盗版泛滥到Win11强制联网:聊聊微软这二十年是怎么用KMS等机制‘围剿’盗版的
  • 一份来自 Karpathy 的 AI 编程 skill
  • 文档地狱求生指南:从“缺失、过时、晦涩”到“清晰、准确、可用”的技术文档治理实战
  • 小龙虾OpenClaw 全方位实战指南:下载、安装、配置豆包 API Key 与高阶使用技巧
  • 从零开始:Icarus Verilog 开源硬件仿真器完全指南 [特殊字符]