08c:private-COW 与 prepared runtime 超小单审查
Current update (
94fab78...): Prepared Family v1 现在以显式 opt-in API 复用这套 substrate:Linux exact-artifact member 可使用 private-COW,portable member 使用 private-copy。两条 lane 共享 input;只有 COW lane 共享 sealed image/page lineage,private-copy 逐 Guest prepare。本文仍是旧基线的 COW substrate 取证,当前 family contract 见09-prepared-family.md。
范围与状态
- Historical fixed-target / Observed:审查任务指定基线
agent-python-runtimecommitb6458b41f0fa32085fdd4cad0a528d98602569d2、tree5e8154fc26527181c66e612584d79dd4e620649d。 - 只读覆盖
runtime/engine/wazero的 private-COW、prepared runtime、直接测试及必要的最小 Wasm fixture。 - 明确不覆盖 workspace、suspension、benchmark catalog;未修改源仓库,未运行 campaign 或 git 操作。
- 本文是机制取证,不把测试 fixture 升格为任意 Python 支持或生产延迟结论。
- 结论状态以
Observed、Framing、Unsupported区分;未将物理准备等同于逻辑 effect。
实际复用的对象
- Host 先编译一次
engine.compiled;复用的是 Wazero 的CompiledModule与线性内存基线,不是可重复服务的同一个 Guest 实例。 cowPreparedRuntime(cow_runtime.go)只要求prepare、close、imageState;Linux 实现把基线封装为cowImage。newCOWPreparedRuntimeWithTrustedSource(cow_runtime_linux.go)先调用既有Engine.newPrepared,再读取canonical.module.Memory()并封存。- 因而复用边界是:编译产物 + 已授权 prepared 的线性内存字节;模块实例、stderr/stdout、temporary 与 request 生命周期仍逐次新建/关闭。
prepared字段(engine.go:203)是普通 prepared runtime 的单个 single-use slot,不是池;takePrepared取走后立即置空。
memfd、MAP_PRIVATE 与 fresh consumer
newCOWImageWithMaximum(cow_memory_linux.go)要求非空、完整 64-KiB Wasm 页,且 maximum 是包含 baseline 的整页范围。- 通过
unix.MemfdCreate("apyrun-cow-image", MFD_CLOEXEC|MFD_ALLOW_SEALING)建立 Host-owned 匿名文件,并Ftruncate到声明的最大线性内存。 - 非零宿主页用
Pwrite写入;零页跳过写入,保留稀疏尾部。Fstat记录AllocatedBytes,不是把 virtual size 当物理占用。 - 写入后加
F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE;baseline 文件随后不可修改、不可伸缩。 - 每个 consumer 的
cowAllocator.Allocate只允许一次,maximum 必须精确等于 image size;随后unix.Mmap(..., PROT_READ|PROT_WRITE, MAP_PRIVATE)。 MAP_PRIVATE让未写入页复用 sealed memfd 的物理页;consumer 首次写某页时由内核产生私有 COW 页。linuxCOWPreparedRuntime.prepare每次都用 allocator 调InstantiateModule,检查/补齐到 sealed baseline,再调用_initialize,最后MADV_DONTNEED丢弃实例化期间的私有脏页。- 丢弃后重新暴露 sealed baseline;得到的
preparedInstance只服务一次,runWithPrepares结束时关闭 module 与 temporary,并释放 COW lease。 - 这是 fresh consumer:即使线性内存来自同一 baseline,也没有把上一个请求的 module、global/table、WASI handle 或 Host context 重新拿来服务。
prepared slot 与端到端生命周期
PrepareSemanticRuntime要求PreparedRuntime且无 workspace/Broker authority;ensurePreparedWithResultAndTrustedSource只初始化一次。MemoryCOW=false时,newPrepared在超时 context 下 instantiate、_initialize、runtime_init,存入单个 slot;第一次请求命中,后续由takePrepared走 fresh instantiate。MemoryCOW=true时,canonical prepared instance 先完成_initialize、runtime_init,可信 source 还会调用runtime_prepare,随后sealCOWPreparedRuntime生成 image;canonical 实例被关闭。Run/runWithPrepares先做 request admission、profile compatibility、workspace admission 与 prepared ensure,再acquireCOWRuntime;COW 命中就 clone 一个新 consumer。- 命中的 consumer 跳过 request-path
_initialize/runtime_init,但仍做callSourceValidation,然后接受 Host-trustedruntime_preparechunk,最后才callExecute。 cowActivelease 保护 image 生命周期;closeCOWRuntime在 active lease 非零时返回errCOWRunsActive,释放后才关闭 active/parent image。SemanticAnalysisSession.ensureModuleLocked走同一 COW prepare;记录COWHits、clone lifecycle 与COWCloneNanos,session 终止时关闭 Guest 并释放 lease。
prepared-data fixture 与 mutation isolation
PrepareNumpyCOWShard只接受已绑定、已验证 artifact/manifest、profile ID 为numpy-core且 qualified import 含numpy的 profile;可信包 source 精确为import numpy as np\n。trustedCOWPrepareIdentity与trustedCOWDerivedIdentity对允许的 source 做 SHA-256;不匹配、NUL、非法 UTF-8、超长或模板外语句返回ErrTrustedCOWPrepareSource。DeriveNumpyI64COWDataset仅接受固定 8-MiB body,编码成固定<i8、(1024,1024)的 Python/NumPy source;这是 research-only fixture,不是通用 object ABI。- derivation 先从 package parent 建 fresh COW consumer,再执行可信
runtime_prepare形成新的 sealed image;cowParentRuntime永久保留包基线,旧 active derived image 关闭。 - 同一
cowImage可有多个独立 mapping;一个 mapping 写入 baseline 或 grow tail,不会改变 sibling。MADV_DONTNEED后私有写入被丢弃,重新看到 sealed baseline。 - growable maximum 之外不可
Reallocate/Grow;maximum 内未在 baseline 的 tail 读为零,避免 consumer 继承未定义尾部。
Linux-only、fallback 与边界
- Current / Observed:
cow_memory_linux.go与cow_runtime_linux.go由//go:build linux保护;cow_runtime_stub.go在非 Linux 直接返回 “memory COW is only available on Linux”。 COWProbe仅把 Linux、单一 exportedmemory、无 imported memory、声明 maximum 的模块标为 candidate;固定/可增长形状另行记录,实际 maximum 仍来自 Wazero memory definition。MemoryCOW必须依赖PreparedRuntime;机制解析层可把 Host 不可用的 COW 记录为fallback/unavailable,不改变 capability grants。- 普通
Run的 COW image 初始化/clone 出错会返回错误,不暗中把 COW 失败伪装成成功;semantic session 对准备失败或 clone 失败可转 fresh Guest,但取消/closing 时 fail closed。 - 显式
SemanticAnalysisSession.Prepare不接受 fresh fallback;容量证据必须是PreparedHit或COWHit且NeverServed,否则返回 capacity unavailable。 - Unsupported:Linux private-COW 不是跨平台保证,也不是完整 interpreter snapshot;代码只封存线性 memory,不证明任意 Guest 的 globals、tables、WASI 资源、clock/random 或 Host-side state 可恢复。
copy / zero-copy 边界
- Copy:canonical Guest memory 通过
memory.Read复制到 Gobaseline,再按页Pwrite到 memfd;derived body 也经 base64/source ABI 进入 Guest。 - Shared/zero-copy-like:同一 image 的未修改线性 memory 页由内核映射共享;每个 consumer 不做 Go 层全量 memory copy。
- Discard:
MADV_DONTNEED只丢弃 consumer 的私有脏页,让下一次 fault 回到 sealed file;它不是逻辑恢复任意 Guest 状态的 API。 - request、prepare source、response、stderr、broker receipt 都仍经过 byte slice/ABI 或 Host buffer;“zero-copy”只能宣称在线性 memory page 层。
关键 symbols、tests 与证据
cow_memory_linux.go:newCOWImageWithMaximum、mapPrivate、restoreBaselineBeforeServe、Free/close。cow_runtime_linux.go:newCOWPreparedRuntimeWithTrustedSource、prepare、derive、sealCOWPreparedRuntime。engine.go:PrepareNumpyCOWShard、DeriveNumpyI64COWDataset、ensurePreparedWithResultAndTrustedSource、takePrepared、runWithPrepares。cow_memory_linux_test.go:trap instantiate 释放 mapping;growable zero tail;sibling isolation/discard;seals/shape;Wazero tiny/growable fixtures。trusted_prepare_test.go:source identity、profile binding、baseline drift、growth pages、package parent 保留与 derived replacement。cow_runtime_test.go:active lease 下 Close、failed clone lifecycle;semantic_lifecycle_test.go:body-free counters 与 authority rejection。runtime/mechanisms_test.go:MemoryCOWdependency 与 unavailable fallback evidence;这些是机器可读 disposition,不是性能测量。
Framing 与答辩一句话
- Framing:private-COW 复用的是既有 Linux memfd/mmap/MADV substrate、Wazero allocator 与 fresh-instance lifecycle;prepared-data 只提供受限 source identity、profile binding 和固定 NumPy derivation fixture。
- 它没有提出通用 prepared-data 存储/恢复语义,也没有证明 arbitrary Python 或多类 Guest mutable state 的快照能力。
- 答辩一句话:我们不是把 prepared data 做成可恢复的通用快照,而是在明确授权的 canonical Guest 上封存线性内存基线,再用 sealed memfd +
MAP_PRIVATE给每次新 Guest 一个可丢弃 consumer;所以贡献边界是既有 substrate 的隔离复用,不是 prepared-data 新语义。