打开文档导航

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-runtime commit b6458b41f0fa32085fdd4cad0a528d98602569d2、tree 5e8154fc26527181c66e612584d79dd4e620649d
  • 只读覆盖 runtime/engine/wazero 的 private-COW、prepared runtime、直接测试及必要的最小 Wasm fixture。
  • 明确不覆盖 workspace、suspension、benchmark catalog;未修改源仓库,未运行 campaign 或 git 操作。
  • 本文是机制取证,不把测试 fixture 升格为任意 Python 支持或生产延迟结论。
  • 结论状态以 ObservedFramingUnsupported 区分;未将物理准备等同于逻辑 effect。

实际复用的对象

  1. Host 先编译一次 engine.compiled;复用的是 Wazero 的 CompiledModule 与线性内存基线,不是可重复服务的同一个 Guest 实例。
  2. cowPreparedRuntimecow_runtime.go)只要求 preparecloseimageState;Linux 实现把基线封装为 cowImage
  3. newCOWPreparedRuntimeWithTrustedSourcecow_runtime_linux.go)先调用既有 Engine.newPrepared,再读取 canonical.module.Memory() 并封存。
  4. 因而复用边界是:编译产物 + 已授权 prepared 的线性内存字节;模块实例、stderr/stdout、temporary 与 request 生命周期仍逐次新建/关闭。
  5. prepared 字段(engine.go:203)是普通 prepared runtime 的单个 single-use slot,不是池;takePrepared 取走后立即置空。

memfd、MAP_PRIVATE 与 fresh consumer

  • newCOWImageWithMaximumcow_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 与端到端生命周期

  1. PrepareSemanticRuntime 要求 PreparedRuntime 且无 workspace/Broker authority;ensurePreparedWithResultAndTrustedSource 只初始化一次。
  2. MemoryCOW=false 时,newPrepared 在超时 context 下 instantiate、_initializeruntime_init,存入单个 slot;第一次请求命中,后续由 takePrepared 走 fresh instantiate。
  3. MemoryCOW=true 时,canonical prepared instance 先完成 _initializeruntime_init,可信 source 还会调用 runtime_prepare,随后 sealCOWPreparedRuntime 生成 image;canonical 实例被关闭。
  4. Run/runWithPrepares 先做 request admission、profile compatibility、workspace admission 与 prepared ensure,再 acquireCOWRuntime;COW 命中就 clone 一个新 consumer。
  5. 命中的 consumer 跳过 request-path _initialize/runtime_init,但仍做 callSourceValidation,然后接受 Host-trusted runtime_prepare chunk,最后才 callExecute
  6. cowActive lease 保护 image 生命周期;closeCOWRuntime 在 active lease 非零时返回 errCOWRunsActive,释放后才关闭 active/parent image。
  7. 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
  • trustedCOWPrepareIdentitytrustedCOWDerivedIdentity 对允许的 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 / Observedcow_memory_linux.gocow_runtime_linux.go//go:build linux 保护;cow_runtime_stub.go 在非 Linux 直接返回 “memory COW is only available on Linux”。
  • COWProbe 仅把 Linux、单一 exported memory、无 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;容量证据必须是 PreparedHitCOWHitNeverServed,否则返回 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 复制到 Go baseline,再按页 Pwrite 到 memfd;derived body 也经 base64/source ABI 进入 Guest。
  • Shared/zero-copy-like:同一 image 的未修改线性 memory 页由内核映射共享;每个 consumer 不做 Go 层全量 memory copy。
  • DiscardMADV_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.gonewCOWImageWithMaximummapPrivaterestoreBaselineBeforeServeFree/close
  • cow_runtime_linux.gonewCOWPreparedRuntimeWithTrustedSourcepreparederivesealCOWPreparedRuntime
  • engine.goPrepareNumpyCOWShardDeriveNumpyI64COWDatasetensurePreparedWithResultAndTrustedSourcetakePreparedrunWithPrepares
  • 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.goMemoryCOW dependency 与 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 新语义。