☰
linux CRIU 工作原理与使用场景
2026/10/13 7:01:41 网站建设 项目流程

CRIU 工作原理与使用场景

1. 核心思想

CRIU(Checkpoint/RestoreInUserspace)要解决的问题是:
如何把一个运行中的进程"拍快照"存到磁盘,之后再原样恢复。

它的突破点在于,一个进程的"状态"几乎全都能通过 Linux 内核已有的接口从用户态读出来和写回去,所以不需要在内核里写一个庞大的 checkpoint 子系统。

一个进程的完整状态包括:

状态类别具体内容
内存代码段、数据段、堆、栈、mmap 区域
CPU 上下文寄存器、FPU 状态、信号掩码
打开的资源文件 fd、socket、pipe、eventfd、timerfd
进程关系PID、PPID、线程、会话、进程组
其他挂载命名空间、cgroup、TCP 连接状态等

CRIU 本身是一个用户态项目/工具(criu.org),不是内核的一部分,但依赖一系列专门的内核接口才能工作。它是Linux 平台专属的机制。


2. Checkpoint(转储)流程

criu dump --tree PID

用 ptrace 冻结整个进程树

通过 /proc/PID 收集信息

/proc/PID/maps 内存布局

/proc/PID/fd 打开的文件

parasite code 注入读内存/寄存器

序列化为 image 文件 *.img

可选: 让进程继续运行 或 杀掉

关键技术点:

  1. 冻结进程:用ptrace(PTRACE_SEIZE)把目标进程及其所有子进程停住。
  2. 收集信息:大量读取/proc/<pid>/下的虚拟文件(maps、fd、stat、status 等)。
  3. 寄生代码(parasite code):CRIU 把一小段代码注入到目标进程内部运行,因为有些信息(如进程内部的内存内容、寄存器)只有"在进程里面"才能拿到。dump 完成后再把这段代码清理掉。
  4. 序列化:所有状态写成一组.img镜像文件(Protocol Buffers 格式)。

3. Restore(恢复)流程

criu restore

读取 image 文件

用 clone 重建进程树
复用原来的 PID

重建地址空间
映射内存页

重新打开文件/socket
恢复 fd

恢复信号/定时器/凭据

morph: CRIU 把自己变成目标进程

恢复寄存器, 从断点继续运行

恢复的精妙之处:

  • 复用 PID:通过/proc/sys/kernel/ns_last_pid或 clone 带指定 PID 的能力,让恢复的进程拿到和原来一样的 PID(很多程序依赖自己的 PID)。
  • “变形”(restorer blob):CRIU 进程本身最后会把自己的地址空间替换成目标进程的,然后跳转到断点继续执行——就像"借壳还魂"。

4. 依赖的内核能力

CRIU 不是内核的一部分,但依赖内核提供这些接口:

  • /proc/<pid>/丰富的进程信息
  • ptrace(冻结、注入)
  • kcmp()系统调用(判断两个 fd 是否指向同一对象)
  • CAP_CHECKPOINT_RESTORE权限(内核 5.9+,专为 C/R 设计)
  • TCP repair 模式(恢复 TCP 连接而不断链)
  • 各种驱动的 C/R 插件接口(如 AMDKFDGPU 插件)

5. 典型使用场景

1. 容器热迁移(最主要用途)

把一个运行中的容器从机器 A不中断地迁移到机器 B。Podman、LXC/LXD、OpenVZ、Kubernetes(Forensic Container Checkpointing)都集成了 CRIU。

# 机器 Acriu dump-t<pid>-D/checkpoint --shell-job# 拷贝 /checkpoint 到机器 B# 机器 Bcriu restore-D/checkpoint --shell-job

2. 长任务的保存/恢复

科学计算、HPC、AI 训练跑几天,担心机器重启或崩溃。做周期性 checkpoint,崩溃后从最近快照恢复,而不是从头再来。这正是 GPU CRIU 的核心价值——ROCm 训练任务可以连 GPU 状态一起保存恢复。

3. 加速启动 / "慢启动"服务

有些程序启动要做大量初始化(加载模型、预热缓存)。可以启动一次 → checkpoint → 之后直接从快照恢复,秒级冷启动。Java/JVM 的 CRaC、FaaS 冷启动优化都用到这个思路。

4. 进程级调试 / 快照回放

把出问题的进程状态 dump 下来,带到别处反复分析、复现 bug。

5. 负载均衡 / 节能

把进程从繁忙节点迁移到空闲节点,或迁移后关闭空闲机器省电。


6. 局限性

  • 仅限 Linux,且对内核版本有要求
  • 跨机器迁移要求两端CPU 架构、内核、库版本基本兼容
  • 某些资源难以恢复:依赖特定硬件的设备 fd、外部网络对端状态、GUI/X11 连接等(这类资源通常需要专门的驱动插件支持)

7. 案例:CRIU 遇到设备 fd 时怎么办

普通进程的资源(内存、普通文件、socket)CRIU 都能自己处理,但当进程打开了一个设备文件 fd(如/dev/kfd、render node、各种加速器节点)时,CRIU 就进入了盲区——它只知道"有一个指向某设备的 fd",完全不了解这个 fd 背后在内核驱动和硬件里关联了哪些状态(显存、队列、映射等)。

下面以 AMD GPU(amdgpu/KFD)为例,说明处理这类设备 fd 的通用模式。

7.1 为什么 CRIU 自己做不了

一个设备 fd 背后的状态往往满足以下任意一点,导致 CRIU 无法独立处理:

特点举例(GPU)
状态不在系统内存里显存缓冲区(VRAM BO)
状态由硬件/固件维护正在被硬件调度的队列、波前上下文
存在驱动内部的句柄/地址映射BO handle、GPU 虚拟地址、doorbell
需要特定时序地暂停/恢复硬件队列 evict(冻结)与 restore(解冻)

这些只有驱动自己知道怎么安全地导出和重建。

7.2 通用解决模式:用户态插件 + 内核 ioctl

CRIU 为此提供插件(plugin)机制,驱动厂商按两层实现:

遇到设备 fd (/dev/kfd)

专用 ioctl (AMDKFD_IOC_CRIU_OP)

criu 主程序

驱动 CRIU 插件
(用户态 .so)

内核驱动
(kfd_ioctl_criu)

导出 / 重建:
显存 BO / 队列 / 事件 / SVM

  • 用户态插件:注册到 criu 的一个.so,声明"这类设备 fd 由我负责",在 dump/restore 时被 criu 回调。
  • 内核态接口:驱动提供一个专用 ioctl,真正完成设备内部状态的导出与重建。

7.3 分阶段协议(以 KFD 为例)

设备状态的 C/R 通常不是一次性完成,而是拆成多个阶段,配合 criu 主流程的节奏:

阶段作用
PROCESS_INFOdump 前:暂停(evict)设备队列,统计需要保存的对象数量和大小,告诉用户态要分配多大缓冲区
CHECKPOINT把设备内部状态(显存、队列、事件、映射)导出到用户缓冲区
UNPAUSEdump 结束后恢复队列运行(若选择让原进程继续)
RESTORE在新进程里按快照重建设备对象,并还原原有的句柄 / 地址
RESUME最后一步:解除对内存通知的阻塞,让进程与设备真正恢复运行

7.4 恢复时的关键约束:一致性

设备 fd 恢复后,应用里保存的各种"指针"必须依旧有效,所以驱动在 restore 时要保证:

  • 句柄不变:缓冲区/对象的 handle 与 checkpoint 前一致
  • 地址不变:GPU 虚拟地址、doorbell 偏移等保持原值
  • 映射关系还原:同一块显存在多设备间的映射要重新建立

这也是为什么设备 C/R 必须由驱动主导——只有它掌握这些内部映射的完整信息。

7.5 小结

CRIU 处理设备 fd 的通用思路是:把自己搞不定的部分"外包"给驱动。
criu 负责整体进程的冻结/恢复与调度,驱动插件负责设备侧状态的导出/重建,两者通过"用户态插件 + 内核 ioctl + 分阶段协议"协作。amdgpu/KFD 的 CRIU 支持正是这一模式的典型实现。


8. 显存数据的存储开销与优化

设备 C/R 必须把显存 BO 里的实际数据(而不仅是元数据)保存下来,否则恢复后 GPU 显存就是一片垃圾值,任务无法正确续跑。显存里装的是真正的计算内容:模型权重、优化器状态、激活值、中间张量等,一个字节都不能少。

8.1 开销有多大

checkpoint 大小基本等于进程占用的显存量:

场景显存占用 ≈ checkpoint 大小
小模型推理几百 MB ~ 几 GB
单卡 LLM 训练20 ~ 80 GB
多卡大模型每卡几十 GB,总量可达数百 GB ~ TB

对大模型训练,一次 checkpoint 几十上百 GB 是正常现象——这是 GPU C/R 的固有成本,不是实现缺陷。

8.2 常见优化手段

手段说明
压缩CRIU 支持镜像压缩;显存数据(尤其含大量零/稀疏)常有可观压缩比
增量 checkpoint只保存相比上次快照变化的页(dirty pages),而非每次全量
快速存储落到 NVMe / 本地高速盘,缩短冻结窗口
降低频率只在迁移、维护窗口、崩溃容错点触发,而非常态化高频保存
应用层替代框架(PyTorch/TF)自带的保存权重机制,只存必要张量,通常远小于全显存

8.3 为什么仍然值得做

  • 对无法改代码的闭源/第三方 GPU 程序,CRIU 是唯一能"冻结续跑"的办法。
  • 对热迁移场景,必须连完整显存状态一起搬,应用层 save/load 做不到无缝。
  • 磁盘空间相对 GPU 算力时间是便宜的——存几百 GB 换回几天的训练进度,很划算。

一句话:显存数据必须全量保存,checkpoint 大小 ≈ 显存占用;业界用压缩、增量、快速存储、降低频率等手段把开销压到可接受范围,用"磁盘空间"换"不中断 / 可恢复"的价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询