数据截至 (上游 commit d7bc4affdac9)
秒级恢复的核心:内存快照 + userfaultfd 惰性缺页
30 秒导读: 一个暂停过的沙箱可能带着几 GB 的 guest 内存。恢复它时,E2B 不把这几 GB 整块读回来——那要花好几秒。它改用一个 Linux 内核特性
userfaultfd:先把 guest 的整片内存标成"缺页"(还没有真实内容),让 VM 立刻跑起来;等 VM 真的访问到某一页时,内核才发一个缺页事件,E2B 的用户态处理器只把那一页从快照里拉出来填进去。绝大多数页永远不会被碰到,于是也永远不用加载。这一章讲清楚"为什么恢复能这么快"。
本章是全仓工程含量最高的一支。它假设你已经读过沙箱生命周期(知道 Firecracker microVM、Pause/Resume 大致是什么);存储侧(内存 diff 如何分层去重、上传下载)只点到为止,细节交给写时复制块存储。
1. 这是什么(零基础也能懂)
先说问题:恢复一个 VM,慢在哪
一个运行中的沙箱,它的"活着"分两半:
| 状态 | 是什么 | 有多大 |
|---|---|---|
| 磁盘(rootfs) | 文件系统 | 交给块存储层,本章不管 |
| 内存(guest RAM) | VM 里进程的堆栈、页缓存、内核数据…… | 几百 MB 到几 GB |
暂停(Pause)时,这几 GB 内存被存成一个内存快照文件。恢复(Resume)时,理论上你得把这个文件的内容重新灌回 VM 的物理内存,VM 才能从暂停的那一刻继续跑。
问题就在这:几 GB 整块读回来,是秒级甚至十几秒级的开销。 而 E2B 卖的就是"沙箱秒起"。整块加载,做不到。
直觉:大部分页,VM 根本不会碰
关键观察是:一个 VM 刚恢复的头几秒,它真正访问到的内存页只是一小撮——正在跑的那个进程的几页栈、几页代码、几页堆。那几 GB 里的绝大部分(睡着的进程、冷的页缓存、内核里没人动的结构),恢复后很久都不会被读到。
于是有了**惰性(lazy / on-demand)**的思路:
恢复时先不加载任何内存。把 guest 的整片内存标成"空的、缺页的",让 VM 立刻开跑。谁被访问到,谁才被加载。
这就像开一个几百页的 PDF:阅读器不会一次把每页都渲染出来,你翻到哪页才渲染哪页。E2B 对 VM 内存干的是同一件事。