Files
SerpMax/docs/01_CRIU_BROWSER_SNAPSHOT.md

25 KiB
Raw Permalink Blame History

ProfileMax:基于 CRIU 的 Chromium 浏览器快照实现方案

  • 文档类型:实现方案 / PoC 计划
  • 状态:已验证(2026-10-20 本机 PoC + node 端到端集成测试通过,实测数据见第 12 节)
  • 目标路径:/home/bigcode/data/dev/bun/SerpMax/docs/01_CRIU_BROWSER_SNAPSHOT.md

1. 目标与范围

ProfileMax 需要保存已启动的 Chromium 浏览器运行现场。示例:浏览器已经打开 Google 并完成搜索,创建快照后结束原浏览器;后续恢复时尽可能直接回到原搜索结果页面,避免再次启动浏览器、打开网站、重新执行页面初始化,从而降低重复作业的启动延迟和 CPU 峰值。

本方案采用 Linux + Chromium + CRIU,先验证技术可行性,再开发快照管理层。

1.1 必须实现

  • 对 Chromium 完整相关进程树执行 checkpoint / restore。
  • 恢复页面当前的内存状态,重点验证 DOM、JavaScript 堆、滚动位置和页面交互。
  • 快照成功后可以结束原进程;恢复时不依赖原 Chromium 进程仍然存在。
  • 保存快照元数据,校验浏览器版本、启动参数和运行环境。
  • 记录创建耗时、恢复耗时、快照大小、内存占用和 CPU 峰值。
  • 恢复失败时能报告具体阶段和错误,后续可回退到普通浏览器启动流程。

1.2 明确不作为目标

  • 不要求保持原 TCP、HTTP/2、WebSocket 等网络连接。 恢复后允许浏览器和页面重新建立连接。
  • 不保证 Google 或其他网站的服务端会话永远有效。
  • 第一阶段不做跨主机迁移、不做快照分叉克隆、不开发通用虚拟机快照系统。
  • 不自行实现进程内存快照引擎;使用 CRIU 完成底层检查点与恢复。
  • 不修改 Chromium 源码,除非 PoC 证明现有接口无法满足必要条件。

网络连接不需要延续,不代表可以直接忽略进程持有的 socket。CRIU 在 dump 时仍必须能处理进程文件描述符及其内核资源。必须通过 PoC 验证连接相关资源的实际处理方式,不能假设不传输 TCP 就一定可以成功 dump。

2. 总体架构

ProfileMax API / Job Manager
        |
        v
Browser Snapshot Manager
        |
        +-- Browser Runtime Manager
        |      +-- Chromium 启动与进程树识别
        |      +-- 固定 Profile / 启动参数 / 显示环境
        |
        +-- Checkpoint Adapter
        |      +-- CRIU pre-dump / dump / restore 能力探测
        |      +-- 命令执行、超时、日志和错误归一化
        |
        +-- Snapshot Store
        |      +-- CRIU image 目录
        |      +-- 元数据与环境指纹
        |      +-- 完整性检查
        |
        +-- Restore Validator
               +-- 进程存活检查
               +-- 浏览器 DevTools / 页面状态检查
               +-- 页面交互与网络重连检查

建议管理层使用项目现有的 Bun/TypeScript 技术栈实现;CRIU 通过受控子进程调用。若当前项目已有统一任务执行或进程管理模块,应复用,不要另建重复框架。所有 CRIU 命令必须由服务端生成参数数组执行,禁止拼接用户输入到 shell 命令。

3. 关键技术约束

3.1 Chromium 是多进程程序

不能只对 Browser 主进程执行快照。需要识别并纳入同一检查点的相关进程,例如:

  • Browser 主进程
  • Renderer 进程
  • GPU 进程
  • Network Service / Utility 等子进程
  • 与浏览器运行现场相关的其他子进程

PoC 需验证 CRIU 采用正确的进程树入口和权限设置,且恢复后进程关系及 IPC 可用。禁止仅凭一个 PID dump 就认为整个浏览器已被完整保存。

3.2 固定运行环境

第一阶段固定以下条件,减少变量:

  • 同一台 Linux 主机、同一内核版本与 CRIU 版本。
  • 同一 Chromium 二进制版本及启动参数。
  • 同一用户、UID/GID、工作目录、环境变量和 Profile 路径。
  • 相同的挂载点、字体、共享库、临时目录和必要设备。
  • 固定显示模式;优先从无头 Chromium 或固定 Xvfb 环境中选定一种进行 PoC,不要在首轮同时测试多种模式。
  • 固定代理配置。代理本身可以在恢复后重新连接,但其配置应保持一致。

快照元数据必须记录上述环境。若恢复环境与快照不兼容,应拒绝恢复并给出明确原因。

3.3 Chromium 沙箱与 GPU

Chromium sandbox、namespace、seccomp、GPU 设备、共享内存及文件描述符可能影响 CRIU 兼容性。

  • 首轮先使用正常安全配置验证。
  • 如果为了定位问题,需要临时关闭 GPU 或在隔离测试环境中调整沙箱,必须作为单独实验变量记录。
  • 不得把 --no-sandbox 当作生产默认参数,也不得为了通过 PoC 而默默降低生产安全边界。
  • 生产部署前必须明确支持矩阵,写明哪些 Chromium 参数和运行模式经过实测。

3.4 网络与 socket 处理

ProfileMax 不需要恢复原 TCP/WS 连接,但进程内仍可能保存 socket 文件描述符、epoll 状态及异步网络请求状态。

实施原则:

  1. 不将网络连接连续性纳入快照成功标准。
  2. PoC 分别测试 CRIU 默认行为,以及在 CRIU 支持和环境允许时对已建立 TCP 连接的处理行为。
  3. 若 dump 因网络文件描述符失败,记录 CRIU 原始错误和对应 FD 类型,再选择可验证的处理方案;不要直接忽略错误。
  4. 恢复后,允许应用层重建连接;对已经失效的请求和 WebSocket,页面自身必须能恢复或重新加载相关数据。
  5. 如需在恢复后关闭陈旧连接,应采用可验证的浏览器/页面层策略;不要从外部任意关闭 Chromium 的 FD,这可能破坏进程内部状态。

3.5 页面状态边界

进程内存快照的目标是保留运行现场,但不能保证每个网站页面都能无缝续跑:

  • 页面已渲染的 DOM 和 JavaScript 堆是重点验证项。
  • 页面计时器、Promise、Service Worker、后台标签页、扩展进程和浏览器内部任务需要单独测试。
  • 恢复后页面可能检测到网络断开、请求失败或登录状态失效。
  • 页面现场恢复与网站业务状态恢复是两回事。服务端数据可能已经改变。

4. 快照目录与元数据

建议每个快照使用独立目录,CRIU image 文件和元数据分离管理:

data/snapshots/
  <snapshot-id>/
    criu/
      # CRIU 生成的 checkpoint image 文件
    manifest.json
    checkpoint.log
    restore.log

manifest.json 建议字段:

{
  "schemaVersion": 1,
  "snapshotId": "uuid",
  "createdAt": "ISO-8601",
  "status": "ready",
  "browser": {
    "name": "chromium",
    "version": "exact-version",
    "executable": "/usr/bin/chromium",
    "rootPid": 1234,
    "launchArgsHash": "sha256"
  },
  "runtime": {
    "kernelRelease": "exact-release",
    "criuVersion": "exact-version",
    "uid": 1000,
    "gid": 1000,
    "profilePath": "/data/profiles/example",
    "displayMode": "xvfb-or-headless",
    "networkContinuityRequired": false
  },
  "metrics": {
    "checkpointDurationMs": null,
    "restoreDurationMs": null,
    "snapshotBytes": null
  },
  "validation": {
    "processTreeRestored": false,
    "pageStateVerified": false,
    "interactive": false
  }
}

上例仅为结构示意。实际实现必须从运行环境采集真实值,不能硬编码 PID、版本或路径。敏感代理凭据、Cookie 和页面数据不得写入普通日志或 manifest。

5. 生命周期与状态机

建议快照状态:

  • creating:正在执行 checkpoint。
  • ready:checkpoint 完成,元数据和文件校验通过。
  • restoring:正在恢复。
  • restored:进程恢复成功且基础检查通过。
  • failed:创建或恢复失败。
  • deleting:正在清理。

创建流程(浏览器关闭前自动快照):

  1. ProfileMax 统一管理浏览器关闭入口。任务完成、任务管理器要求关闭或用户主动关闭时,先进入 closing 流程;不要等 Chromium 已经开始退出后才监听 close 事件。
  2. 检查目标浏览器实例和所有权,并确认任务已结束、没有正在执行的关键操作。
  3. 记录 Chromium 版本、进程树、启动参数、运行环境及当前作业 URL。
  4. 创建临时快照目录并执行 CRIU checkpoint/dump。
  5. 收集退出码、stdout/stderr、CRIU 日志和耗时;校验检查点文件完整并写入 manifest。
  6. 校验通过后,原子地将快照标记为 ready,并将其关联到对应浏览器实例/任务。
  7. 只有快照已可靠落盘并标记为 ready 后,才完成浏览器关闭流程。注意:常规 CRIU dump 通常会让被 dump 的进程停止/退出,因此应由 CRIU 的实际 dump 语义完成进程收尾,不能再无条件调用 Chromium 的正常关闭。
  8. 如果快照失败,记录错误并按配置执行普通关闭;不要把不完整快照标记为 ready。若产品要求必须先保留一个仍运行的 Chromium 再正常关闭,需要单独验证 leave-running 模式与 Profile 文件一致性,不能直接把它当作安全默认方案。

关闭事件的实现要求:使用 ProfileMax 自己的统一关闭方法/生命周期钩子作为触发点;浏览器进程已经退出后的 close/exit 通知只用于清理和记录结果,不用于启动快照。

恢复流程:

  1. 校验 manifest、快照文件、版本和运行环境。
  2. 确保没有冲突的同一实例正在运行;首版不支持从同一快照并发恢复多个实例。
  3. 准备恢复所需的用户、目录、挂载、网络命名空间和显示环境。
  4. 执行 CRIU restore,保存日志和耗时。
  5. 验证恢复后的进程树、浏览器调试接口(如果启用)、原页面状态及用户交互。
  6. 对网络重连进行观察,但不要求旧连接继续存在。
  7. 只有验证通过才标记为 restored;失败时保留诊断材料,避免自动覆盖原快照。

注意:CRIU 的具体命令行参数、权限要求和网络选项必须以部署机器上安装版本的 criu --help、官方文档和 PoC 结果为准。本文不预设某一组命令在所有内核/CRIU 版本上通用。

6. 模块接口建议

管理层可先实现以下内部接口,具体参数根据现有 ProfileMax 代码规范调整:

type SnapshotStatus =
    | "creating"
    | "ready"
    | "restoring"
    | "restored"
    | "failed"
    | "deleting";

interface SnapshotManifest {
    schemaVersion: number;
    snapshotId: string;
    createdAt: string;
    status: SnapshotStatus;
    browser: {
        name: "chromium";
        version: string;
        executable: string;
        rootPid: number;
        launchArgsHash: string;
    };
    runtime: {
        kernelRelease: string;
        criuVersion: string;
        uid: number;
        gid: number;
        profilePath: string;
        displayMode: string;
        networkContinuityRequired: false;
    };
}

interface CheckpointResult {
    snapshotId: string;
    durationMs: number;
    snapshotBytes: number;
    logPath: string;
}

interface RestoreResult {
    snapshotId: string;
    durationMs: number;
    restoredPids: number[];
    validationPassed: boolean;
}

interface CheckpointAdapter {
    checkCapabilities(): Promise<{
        available: boolean;
        criuVersion?: string;
        reasons: string[];
    }>;

    checkpoint(browserRootPid: number, snapshotDir: string): Promise<CheckpointResult>;

    restore(snapshotDir: string): Promise<RestoreResult>;
}

这些接口是设计草案,不代表项目中已经存在对应模块。需要先查看 ProfileMax 当前目录结构和现有进程管理实现,再决定最终落点。

7. PoC 实施计划

P0:环境检查

  • 确认 Linux 发行版、内核版本、CRIU 版本及所需权限。
  • 运行 CRIU 自检,并保存完整输出。
  • 检查 Chromium 的实际进程树、命名空间、打开的 FD、共享内存和设备依赖。
  • 记录当前 Chromium 版本、启动参数和运行模式。

交付物:环境报告、CRIU capability 报告、浏览器进程树样本。

P1:最小 checkpoint / restore

  • 启动 Chromium 并打开固定测试页面。
  • 在页面内创建可验证的 JavaScript 状态标记,例如计数器、随机 token、滚动位置和 DOM 文本。
  • 执行 checkpoint,确认原实例已按预期停止或退出。
  • restore 后读取页面标记,验证其与 checkpoint 前一致。
  • 检查页面是否可点击、输入和滚动。

交付物:可重复执行的测试脚本、CRIU 日志、成功/失败原因。

P2:真实搜索页面

  • 打开 Google 并执行固定关键词搜索,按正常流程完成测试任务。
  • 任务完成、浏览器准备关闭时,由 ProfileMax 关闭流程触发快照;不使用定时快照,也不需要单独监听页面稳定事件。
  • 快照完成并可靠落盘后,按 CRIU dump 的实际语义结束原进程,再执行恢复测试。
  • 验证 URL、DOM、滚动位置和页面交互。
  • 观察网络重连后的页面行为;不把旧 TCP/WS 保持连接作为成功条件。

交付物:真实网站验证记录。若网站行为不稳定,使用本地测试页面作为可重复基线,并将网站测试作为补充。

P3:基准测试

至少重复 20 次,分别测量普通冷启动和快照恢复:

指标 冷启动 CRIU 恢复
总耗时 P50 / P95 测量 测量
CPU 峰值 测量 测量
恢复后可交互耗时 测量 测量
快照体积 不适用 测量
失败率 测量 测量

必须统一测试条件:相同主机、Chromium 版本、Profile、页面、代理和资源状态。单次结果不能作为结论。

P4:管理器封装

只有 P1/P2 证明可行后再实施:

  • snapshot create / restore / inspect / delete。
  • 状态机和并发锁。
  • 日志、错误分类、超时和取消。
  • 快照容量限制及过期清理。
  • 环境兼容性校验和失败降级。
  • 权限隔离,避免普通用户对任意系统进程执行 dump/restore。

8. 验收标准

首阶段必须全部满足:

  1. 能对目标 Chromium 进程树成功 checkpoint。
  2. 原浏览器进程停止后,能够从快照恢复。
  3. 恢复后原页面的验证标记、URL、主要 DOM 状态及滚动位置符合预期。
  4. 页面可继续交互,且允许网络连接重新建立。
  5. 失败时能够通过日志定位到具体 CRIU 错误或资源依赖。
  6. 测得冷启动与恢复的耗时、CPU 峰值、快照大小和失败率。

性能目标在取得基线数据后设定,不预先承诺快照恢复一定比冷启动快。快照读取、内存恢复和页面恢复验证都可能消耗显著的磁盘 IO 与 CPU。

9. 安全与运维要求

  • 快照可能包含 Cookie、认证令牌、页面内容及其他敏感内存数据,按敏感数据保护。
  • 快照目录仅允许浏览器运行用户和快照管理服务访问,权限默认最小化。
  • 不在日志中输出 Cookie、Authorization、代理密码或页面表单内容。
  • 只允许管理器操作其登记的 Chromium 进程,不接受任意 PID 的外部请求。
  • CRIU dump/restore 使用最小必要权限;如需 root 权限,限定服务边界并记录审计日志。
  • 删除快照时清理完整目录,并记录清理结果。
  • 版本、内核或启动参数不兼容时拒绝盲目恢复。

10. 参考资料

11. 当前结论

采用“CRIU 底层 + ProfileMax 自有管理层”的路线。第一阶段只交付 PoC,不提前承诺 Chromium 全进程树一定可恢复。优先验证真实运行环境中的沙箱、GPU、共享资源和 socket 处理。TCP/WS 连续性不是验收目标;进程仍需正确处理网络相关 FD,恢复后允许应用重新连接。PoC 通过后再扩展为多实例快照管理。

12. 实测结论与落地记录(2026-10-20)

12.1 环境与实测数据

  • 环境:Zorin OS 18.1(Ubuntu noble 基),内核 7.0.0-31-generic,veil-chromium 153.0.8010.52; CRIU 4.2(源码编译,/usr/local/bin/criu,文件能力 cap_sys_ptrace,cap_sys_admin,cap_net_admin+eip, 非 root 可用,免改 ptrace_scope)。
  • PoC(直启 chrome → baidu 搜索页 → dump → restore):
指标 实测值
冷启动(进程拉起 → CDP 就绪) ≈ 0.6s
dump(9 进程树,RSS ≈ 1.13GB) ≈ 1.3s,镜像 ≈ 354MB(397 个 .img)
restore(detached) ≈ 0.36s
现场校验 URL/标题/JS 堆标记/滚动位置/搜索结果 DOM/localStorage 全部一致,navigator.onLine=true
可交互 恢复后二次搜索成功(网络自动重连)
  • node 端到端(经 ProfileMax API:创建快照 Profile → 启动 → 搜索 → 停止自动 dump → 再启动): 停止含 dump ≈ 1.3s;再次启动 ≈ 0.4s 且 restored:true,CDP 端口原样恢复,现场校验同上全绿 (复测脚本 node/scripts/criu-poc.ts、node/scripts/criu-e2e.ts)。

12.2 关键结论(实测定论)

  1. 沙箱与 CRIU 的结构性冲突:criu 拒绝对「非根进程自建的嵌套 PID namespace」执行 dump (CLONE_SUBNS 仅含 mount/net),而 chromium namespace 沙箱的 zygote/渲染进程恰在嵌套 pidns 中。 → 快照模式 Profile 启动时显式追加 --no-sandbox(browser.manager snapshotSandboxArgs); 生产节点为 root 容器,ProfileMax 本就强制 --no-sandbox,此处仅影响非 root 环境,非全局降级。
  2. 仅支持 headless:有头模式连接的 X11/Wayland socket 是树外 STREAM unix socket, criu 的 --ext-unix-sk 仅支持 DGRAM 外部连接 → 有头实例停止时不做快照(回退常规关闭)。
  3. D-Bus 必须「干净失败」隔离:chrome 对已建立后断开的 dbus 连接会 FATAL ("D-Bus connection was disconnected. Aborting.")。快照模式启动 env 将 DBUS_SESSION_BUS_ADDRESS/DBUS_SYSTEM_BUS_ADDRESS 指向 unix:path=/nonexistent-bus (与生产容器无 bus 的现实一致);地址格式非法(如 /dev/null)会导致导航失败,不可用。
  4. crashpad handler 在树外:chrome 会把 handler daemonize 到进程树外(subreaper 接管), 其 SEQPACKET socketpair 端为树外对端。已给 criu 打补丁:dump 允许外部 STREAM/SEQPACKET, restore 侧对非 DGRAM 外部对端跳过 connect(以「未连接」语义恢复,客户端优雅降级); 补丁内容两处(criu/fsnotify.c 悬空 inotify watch 跳过 + criu/sk-unix.c 外部 socket 放行), 生产镜像需携带补丁版 criu。
  5. chromium 共享内存临时文件(.org.chromium.Chromium.*,实测 4MB 起步)远超 criu 默认 1MB ghost 限额 → dump/restore 统一 --ghost-limit=512MB(env CRIU_GHOST_LIMIT_MB 可调)。
  6. dump 参数:--file-locks --tcp-close --ext-unix-sk --ghost-limit=N [--unprivileged]; --tcp-close 表示已建 TCP 连接按「关闭」落镜像(符合 1.2 节「不要求连接连续性」), 恢复后由页面/浏览器自行重连,实测 baidu 页面正常。
  7. 启动方式约束:快照 Profile 由本服务直启(setsid 保证 session leader + 显式 --user-data-dir),不经 playwright 管道(管道对端在树外无法 dump);CDP 经 connectOverCDP 挂接,停止时 playwright 句柄 close() 仅断连不杀进程。
  8. 参数哈希与端口绑定:dump 时将完整启动参数(含 --user-data-dir 打头)哈希入 manifest, restore 前重算比对,任一不一致拒绝恢复回退冷启动;impersonate MITM 端口记入 manifest, 恢复时 ensureImpersonate 优先复用同端口(浏览器重连无缝),端口被占则拒绝恢复。
  9. 陈旧 DevToolsActivePort:dump 后该文件残留,直启前必须删除,否则 CDP 就绪探测 可能打在死端口上(实测踩坑)。
  10. criu 单次操作超时 180s,dump/restore 失败不重试:回退常规关闭/冷启动, 失败快照保留 failed 状态与日志供诊断(manifest.json / dump.log / restore.log)。

12.3 落地范围(本版)

  • Profile 新增 snapshot.enabled 配置(admin「快照模式」开关,缺省关闭;仅 veil + headless 生效);
  • 停止浏览器时自动 checkpoint(存储快照抓取先于 dump),启动时优先 restore 并回写 CDP/条目;
  • 节点 API:GET/DELETE /profiles/:id/snapshot;admin 中继同路径 GET/DELETE + PUT 快捷开关;
  • admin 概览卡:快照(含大小)/ 自动CF挑战 / 自动挑战 reCAPTCHA 三开关同行;
  • 快照目录:<profileDir>/<profileId>/snapshot/(manifest.json + criu/ 镜像 + dump/restore 日志), 随 Profile 删除一并清理;同一 Profile 仅保留最近一次快照。

13. 镜像压缩与 USER_DATA 清理(2026-10-21,node v0.38.0 / admin v1.55.0)

13.1 镜像压缩(zstd 后处理)

CRIU 4.x 核心已移除内建压缩选项(旧版 -z/--compress 不存在,criu-image-streamer 需额外组件), 采用「dump 后 zstd 压缩大镜像 / restore 前解压还原」的后处理方案(snapshot.manager):

  • dump 成功后(进程已退出)对镜像目录中 >1MB 的 *.img 逐文件 zstd -3 -T0 压缩, 压缩成功才删原文件,单文件失败保留原样不阻塞;SNAPSHOT_COMPRESS=off 可整体关闭。
  • restore 前把 *.img.zst 解压还原(criu 不识别压缩镜像),耗时记入 manifest metrics.decompressDurationMs;压缩前/后体积分别记 snapshotBytesRaw/snapshotBytes。
  • 实测(同环境同场景,baidu 搜索现场,两轮):
指标 压缩前(10-20 基线) 压缩后(10-21 实测)
快照镜像大小 308~312MB 67.5~70.6MB(约 4.4x 缩小)
dump 耗时 ≈1.0~1.3s ≈1.0~1.3s(不变)
停止总耗时(含 dump+压缩) ≈1.2~1.3s ≈2.4s(压缩约 +1.1s,zstd -3 -T0)
restore(criu 自身) ≈0.3s ≈0.3s(不变)
restore 前解压 — ≈0.58s
再次启动总耗时 ≈0.43~0.58s ≈1.11~1.16s(解压 +0.6s)
现场校验(8 项) 全绿 全绿(URL/标题/JS 标记/滚动/localStorage/结果数/在线/二次搜索)

结论:以「每次停止 +≈1.1s、每次恢复 +≈0.6s」换「磁盘占用 -77%」,默认开启; 磁盘敏感场景收益显著,对恢复可用性无影响。

13.2 USER_DATA 静态缓存清理

  • 自动清理(默认开启):浏览器停止时实测 USER_DATA 体积(关闭前采样)超过 USERDATA_CLEAN_THRESHOLD_MB(默认 100MB)→ 进程完全退出后软清理静态缓存 (cache.manager autoCleanOnStop);清理后体积才回传 admin(profiles.user_data_size)。
  • 软清理范围(CACHE_DIRS):根级 + Default/ 子目录双布局——HTTP Cache、Code Cache、 GPUCache、Dawn/GrShader/Graphite 缓存、Media Cache、Shared Dictionary、组件 crx 缓存等; Cookie/LocalStorage/IndexedDB/Service Worker/登录态一律保留(实测主要占用 Default/Cache + Default/Code Cache,约占 USER_DATA 80%)。
  • 手动清理:节点 POST /profiles/:id/cleanup(admin 中继 POST /api/profiles/:id/cleanup, Profile 详情页「编辑」与「复制」之间的「清理」按钮):软清理 + 删除现有 CRIU 快照 (快照镜像含缓存页,restore 会原样还原缓存,不删等于没清;下次停止自动重建快照); 浏览器运行中节点拒绝(CACHE_CLEAR_FAILED,提示先停止)。返回 before/after/freedBytes 与 snapshotDeleted,前端 toast 展示释放体积。
  • 验证:130MB 假缓存(Default/Cache)触发清理释放 120MB,Cookie/LocalStorage 保留; 50MB 未触发;E2E 全流程(含手动清理后快照 bytes=0)通过。

13.3 启动方式对比:正常冷启动 vs 快照恢复(到达同一现场)

口径:从 POST /browser/start 发起,到「baidu 同一关键词搜索结果页 DOM 已渲染」为止 (CDP 连接与现场确认均计入);基准脚本 node/scripts/criu-bench-compare.ts,两轮实测:

阶段 冷启动(非快照) 快照恢复
启动 → CDP 就绪 0.80~0.96s 0.99~1.51s(含解压≈0.6s + restore≈0.3s)
导航加载搜索结果 2.52~4.08s(随网络波动) 0(现场已在,免导航)
现场确认 0.08~0.11s 0.04~0.32s
到达同一现场总计 3.69s / 4.44s 1.18 / 1.40 / 1.49 / 1.59s(均值 ≈1.4s)
现场一致性 — 4/4 轮 URL/标题/结果数完全一致
加速比 — 2.86x / 2.88x(两轮稳定)

附:快照 Profile 自身冷启动(3.61s/5.14s)与普通 Profile 无差异,快照模式不拖累冷启动; 本组 dump 耗时 1.7~3.3s、快照 60.4~70.9MB(压缩后),dump 耗时随当时浏览器内存占用浮动。