首页
/ Firecracker 快照版本化(Snapshot Versioning)深入解析:文件格式、编码与兼容性约束

Firecracker 快照版本化(Snapshot Versioning)深入解析:文件格式、编码与兼容性约束

2026-09-08 21:13:22作者:董灵辛Dennis

本文系统讲解 Firecracker 将 microVM 状态持久化为快照的完整机制,涵盖快照文件格式、bitcode 编码选型、MAJOR.MINOR.PATCH 版本判定规则,以及宿主内核、设备模型、CPU 模型三个维度的兼容性约束。读完本文,读者将能理解快照文件每个字段的含义、为何 microVM 状态文件需要独立于 Firecracker 版本存在,并能借助 --snapshot-version / --describe-snapshot 等命令行工具与源码入口快速定位与排查快照兼容性问题。

引言:什么是 Firecracker 快照版本化

Firecracker 使用 serde 配合 bitcode 格式将 microVM 状态序列化为 Firecracker 快照。一个关键设计是:快照本身携带的版本号与 Firecracker 产品版本相互独立。每个 Firecracker 版本会声明它支持某一特定的快照数据格式版本;创建快照时使用当前支持的格式版本写入文件,加载快照时则校验文件格式与当前支持版本是否兼容。

从源码看,当前(本仓库)Firecracker 支持的是快照数据格式版本 11.0.0,该常量定义在 src/vmm/src/persist.rs

/// Snapshot version
pub const SNAPSHOT_VERSION: Version = Version::new(11, 0, 0);

快照版本化的详细规范是本文所依据的原始文档 docs/snapshotting/versioning.md,与其配套的整体快照功能说明见 docs/snapshotting/snapshot-support.md(其中的 Snapshot versioning 小节 也对数据格式版本做了概括性描述)。

microVM 状态的两个组成部分

Firecracker 将 microVM 状态持久化为两个相互独立的对象:

对象 内容
guest memory(客户机内存)文件 以所有页面 dump 的形式保存的 microVM 内存
microVM state(microVM 状态)文件 VMM 内部状态(设备模拟、KVM 与 vCPU)

⚠️ 注意:挂载到 microVM 上的块设备并不属于状态的一部分,必须单独管理。

在 VM 状态文件中,Firecracker 保存了除两项之外的全部 VMM 内部状态——即**串口模拟(serial emulation)**与 **vsock 后端(vsock backend)**被显式排除在外。

由于 Firecracker 会持续通过新增能力、设备或增强来改进功能,microVM 状态文件的结构与语义可能随每个新版本发生改变,这正是独立的快照格式版本存在的根本原因。

与这两个对象对应的状态结构体定义在 src/vmm/src/persist.rs

/// Contains the necessary state for saving/restoring a microVM.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MicrovmState {
    /// Miscellaneous VM info.
    pub vm_info: VmInfo,
    /// KVM KVM state.
    pub kvm_state: KvmState,
    /// VM KVM state.
    pub vm_state: VmState,
    /// Vcpu states.
    pub vcpu_states: Vec<VcpuState>,
    /// Device states.
    pub device_states: DevicesState,
}

可见状态文件顶层承载了 VM 元信息(内存大小、SMT、CPU 模板、引导源、大页配置,见 VmInfo)、KVM 状态、vCPU 状态集合与设备状态集合,结构十分清晰。

microVM 状态文件格式

一个 Firecracker 快照文件整体布局如下:

字段(Field) 位宽(Bits) 描述
magic_id 64 Firecracker 快照标识与架构(x86_64 / aarch64)
version M 快照数据格式版本(MAJOR.MINOR.PATCH
state N 含 microVM 状态的 bitcode blob
crc 64 可选(optional)的 CRC64 校验和,覆盖 magic_id、version 与 state

格式自带的版本号紧随 magic_id 之后编码在文件中,形式为 MAJOR.MINOR.PATCH,独立于 Firecracker 版本。

源码层的格式落地

该布局在源码中的 doc 注释被明确画出,见 src/vmm/src/snapshot/mod.rs

|-----------------------------|
|       64 bit magic_id       |
|-----------------------------|
|       version string        |
|-----------------------------|
|            State            |
|-----------------------------|
|        optional CRC64       |
|-----------------------------|

与文件格式表一一对应的两个关键类型如下(src/vmm/src/snapshot/mod.rs):

  • magic_id:按目标架构硬编码的常量。x86_64 为 0x0710_1984_8664_0000u64,aarch64 为 0x0710_1984_AAAA_0000u64,定义于 src/vmm/src/snapshot/mod.rs。因此 magic 值本身就编码了"这是 Firecracker 快照"以及"快照属于哪种 CPU 架构"两层信息。
  • header:即 SnapshotHdr { magic: u64, version: Version },version 使用 semver::Version 类型承载 MAJOR.MINOR.PATCH
  • dataSnapshot<Data> 中的泛型数据,Firecracker 实际保存时使用 MicrovmState
#[derive(Debug, Serialize, Deserialize)]
struct SnapshotHdr {
    /// magic value
    magic: u64,
    /// Snapshot data version
    version: Version,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Snapshot<Data> {
    header: SnapshotHdr,
    /// The data stored int his [`Snapshot`]
    pub data: Data,
}

加载时的版本兼容判定规则

加载快照时的校验逻辑位于 src/vmm/src/snapshot/mod.rs

if snapshot.header.magic != SNAPSHOT_MAGIC_ID {
    return Err(SnapshotError::InvalidMagic(snapshot.header.magic));
}

if snapshot.header.version.major != SNAPSHOT_VERSION.major
    || snapshot.header.version.minor > SNAPSHOT_VERSION.minor
{
    return Err(SnapshotError::InvalidFormatVersion(
        snapshot.header.version.clone(),
    ));
}

可以推断出 Firecracker 的版本兼容判定精确规则:

  • MAJOR 必须严格相等:不同 major 版本之间互不兼容;
  • MINOR 只能小于或等于当前支持版本的 minor;
  • PATCH 无任何限制:在当前 major.minor 之内,任意 patch 版本均可加载。

这一规则在模块内的单元测试 src/vmm/src/snapshot/mod.rs 中被系统验证:major 加一被拒绝、minor 大于当前被拒绝、minor 小于等于当前可加载、任意 patch(包括 0、当前 +1、乃至 1024)都可加载。

CRC64 完整性校验

CRC 是可选的文件尾部字段,实际实现中保存路径总会写入。保存时通过 CRC64Writersrc/vmm/src/snapshot/crc.rs)在序列化写入的同时累计计算 CRC64,最后把校验和以小端原始字节追加到文件尾部:

pub fn save<W: Write>(&self, writer: &mut W) -> Result<(), SnapshotError> {
    let mut crc_writer = CRC64Writer::new(writer);
    serialize(self, &mut crc_writer)?;
    // Write the CRC as raw bytes, not bitcode-serialized
    crc_writer
        .writer
        .write_all(&crc_writer.checksum().to_le_bytes())
        .map_err(SnapshotError::Io)
}

加载时(src/vmm/src/snapshot/mod.rs),Firecracker 先拆分出末尾 8 字节作为 CRC,对剩余数据做 load_without_crc_check,然后利用 CRC 的性质 crc64(0, buf) == 0(当 buf 末尾 8 字节正是前序内容的校验和时成立)来整体校验,校验失败返回 SnapshotError::Crc64。测试 test_bad_crc 通过翻转文件末尾 8 字节来确认损坏文件能被正确拒绝。

防滥用限制

快照反序列化设有 10 MB 的硬性大小上限SNAPSHOT_DESERIALIZATION_BYTES_LIMIT,见 src/vmm/src/snapshot/mod.rs),目的是防止针对快照加载接口的 DoS 攻击。超过上限即返回 SnapshotError::SizeLimitExceeded。注意快照属于 microVM 状态文件范畴,guest 内存文件不在此限制内(内存文件大小取决于分配给 guest 的内存)。加载与探测版本入口都会先施加该上限检查。

命令行快速查看版本

Firecracker 二进制(src/firecracker/src/main.rs)提供两个与快照版本直接相关的参数:

  • --snapshot-version:打印当前 Firecracker 二进制支持的快照数据格式版本(println!("v{SNAPSHOT_VERSION}"),见 main.rs);
  • --describe-snapshot <path>:读取给定快照状态文件并打印其携带的数据格式版本(经 get_format_version 解析,见 main.rsmain.rs)。

这两个参数在排查"快照在哪个版本创建、当前版本能否加载"的场景中非常实用:先用 --describe-snapshot 看文件的格式版本,再与 --snapshot-version 输出的当前支持版本对照 major/minor,即可预判兼容性。

VM 状态编码:为什么选择 bitcode

在研究原型阶段,Firecracker 团队比较过多种存储格式,评选标准包括:性能、体积、Rust 支持、规范说明、版本化支持、社区与工具链。其中性能、体积与 Rust 支持属于硬性要求,其余标准允许权衡取舍。(该选型比较的更多细节可回溯查看 docs/snapshotting/design.md 中关于 snapshot format 的章节。)

采用 bitcode 的核心收益:

  • 快照体积开销最小:几乎不产生额外元数据冗余;
  • CPU 开销最小:编码/解码耗时极低,利于冷启动与快速恢复场景;
  • 实现简单:直接建立在 serde 的 Serialize / Deserialize trait 之上,接入成本低。

当前实现依赖 Serde bitcode encoder,见 src/vmm/src/snapshot/mod.rs 的模块说明。需要注意的客观局限是:bitcode 的编码格式不允许对状态做向后兼容的增量变更,因此 microVM 状态描述的任何改动基本都会导致格式 MAJOR 版本提升。如果未来确有需要,Firecracker 会评估允许更高向后兼容灵活度的替代格式,届时将重新定义快照格式变更如何映射到 MAJOR.MINOR.PATCH 版本变化。

从源码结构看,快照读写完全建立在 serde 派生机制之上:MicrovmState 及各设备状态结构体均 #[derive(Serialize, Deserialize)],保存路径调用 bitcode::serializesnapshot/mod.rs),加载路径调用 bitcode::deserialize。整条链路上 Firecracker 通过 Snapshot::new(microvm_state).save(&mut snapshot_file) 完成落盘(见 persist.rs)。

快照兼容性

宿主内核(Host kernel)

  • 在同一内核版本上创建与恢复快照没有任何问题
  • 即便使用相同版本的 Firecracker,在不同宿主内核版本之间恢复快照也可能出现问题;
  • 跨不同宿主内核执行 SnapshotCreate / SnapshotLoad 在 Firecracker 中被视为不稳定操作——因为保存下来的 KVM 状态在不同内核上可能具有不同语义。

设备模型(Device model)

当前 Firecracker 设备向后兼容到引入该设备的那个版本为止。理想情况下该性质应长期保持,但存在例外:当某设备的新版本向 guest 暴露了旧版本中不存在的特性时,就无法在不破坏 guest 工作负载的前提下,把快照恢复到更旧的版本上。

microVM 状态文件会以**外链(外部资源)**的方式引用部分快照之外的资源:

  • tap 设备:按设备名引用;
  • 块设备:按块文件路径引用;
  • vsock 后端:按 Unix 域套接字名称引用。

因此成功恢复 microVM 前必须检查:

  1. tap 设备可用、名称与原始名称一致(状态文件中保存的正是这些名称值),且对执行恢复的 Firecracker 进程可访问;
  2. 块设备以其原始相对或绝对路径设置好并带正确的权限——新 Firecracker 进程会像原进程一样精确按原路径去访问它们;
  3. vsock 后端 Unix 域套接字可用、名称与原始名称一致,且对新的 Firecracker 进程可访问。

CPU 模型(CPU model)

Firecracker microVM 快照功能适用于支持硬件虚拟化扩展的 Intel / AMD / ARM64 CPU 模型,受支持平台清单见 README.md。兼容性要点:

  • 跨 CPU 架构不兼容,同一架构内跨 CPU 模型也不兼容
  • 只有快照创建与恢复时暴露给 guest 的 CPU 特性集合是**不变量(invariant)**时才兼容;
  • 最稳妥的平凡场景是:在具有相同 CPU 模型的宿主上创建并恢复快照;
  • 在 Intel 宿主上创建的快照拿到 AMD 上恢复(反之亦然)不受支持

此外需要特别指出:guest 工作负载仍可能执行被 CPU 模板 通过 CPUID 屏蔽(masked)的指令,对这类工作负载做保存与恢复会产生未定义结果。Firecracker 只从 KVM 取回一个离散列表中的 MSR 状态,具体对应 guest 暴露特性集合的 MSR。

与之配套的恢复期安全校验可见源码中的厂商一致性检查:x86_64 平台上 Firecracker 通过 validate_cpu_vendorsrc/vmm/src/persist.rs)比对快照中 vCPU 的 CPUID 厂商 ID 与宿主厂商 ID,aarch64 上则读取宿主制造商 ID 做类似核验——这与文档所述"跨厂商不兼容"在实现层相互印证。

实现:snapshot crate 与 Persist trait

microVM 状态文件格式在 Firecracker 仓库中实现于 snapshot cratesrc/vmm/src/snapshot/mod.rs),其中聚合了:

  • Snapshot<Data>:带 header 的类型化快照容器;
  • SnapshotHdr:magic + version 头;
  • SnapshotError 错误体系:覆盖 Crc64InvalidFormatVersionInvalidMagicBitcodeIoSizeLimitExceeded 六类失败;
  • get_format_version:读取快照文件并返回其格式版本(供 --describe-snapshot 使用);
  • CRC64Writer(独立文件 src/vmm/src/snapshot/crc.rs)。

所有 Firecracker 设备都实现 Persist traitsrc/vmm/src/snapshot/persist.rs),该 trait 暴露了从 microVM 状态创建设备、以及把设备保存为状态的统一接口:

pub trait Persist<'a>
where
    Self: Sized,
{
    /// The type of the object representing the state of the component.
    type State;
    /// The type of the object holding the constructor arguments.
    type ConstructorArgs;
    /// The type of the error that can occur while constructing the object.
    type Error;

    /// Returns the current state of the component.
    fn save(&self) -> Self::State;
    /// Constructs a component from a specified state.
    fn restore(
        constructor_args: Self::ConstructorArgs,
        state: &Self::State,
    ) -> Result<Self, Self::Error>;
}

该 trait 在 virtio 设备族中有大量实现,例如 src/vmm/src/devices/virtio/block/persist.rssrc/vmm/src/devices/virtio/net/persist.rssrc/vmm/src/devices/virtio/vsock/persist.rssrc/vmm/src/devices/virtio/balloon/persist.rs 等。每个设备只需通过 Persist 定义自己的 State 类型与 save / restore 语义,就可被上层 MicrovmState 中的 DevicesState 统一收集并整体序列化。

整条创建链路由 persist.rs 中的 create_snapshot 组织:先调用 vmm.save_state() 获得 MicrovmState 并写入状态文件,再从 KVM VM 把 guest 内存写入 mem 文件,最后对已激活设备重新标记 virtio queue 内存为 dirty 以保证后续 diff 快照正确性。对快照做深入编辑(如改动 vCPU 状态、内存或仅查看信息)可进一步参考 snapshot-editor 文档与 snapshot-editor 工具源码。

小结

  • Firecracker 快照由 guest 内存文件 + microVM 状态文件组成;状态文件采用 magic_id(64) + version(MAJOR.MINOR.PATCH) + bitcode state + CRC64(64) 布局,CRC64 可选但写入路径总会生成。
  • 快照格式版本独立于 Firecracker 版本,当前仓库支持 11.0.0;加载时要求 major 严格相等、minor 不大于当前值、patch 不受限。
  • 编码采用 serde + bitcode,以体积小、CPU 开销低、实现简单为主要考量,代价是任何状态结构变化都会推动 major 版本升级。
  • 兼容性受三方面约束:宿主内核(跨内核保存/恢复不稳定)、设备模型(向后兼容至引入设备的版本;tap/块设备/vsock 套接字等外部资源需按原名/原路径可访问)、CPU 模型(禁止跨架构、跨厂商,CPUID 屏蔽的指令集差异可能导致未定义结果)。
  • 实现层面集中在 snapshot crate,所有设备通过 Persist trait 接入统一的 save/restore 接口。

需要进一步了解快照整体操作流程(Pause / CreateSnapshot / LoadSnapshot 等 API 使用)的读者,可直接阅读配套文档 docs/snapshotting/snapshot-support.md

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393