PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
DEEP DIVE / 00

深度解析:从构建到状态复用

沿真实实现追踪数据、控制流和内存。

本章目录

这组长文追踪一套具体实现:代码在何处生成,值如何跨过 Go、Wasm 和 CPython,哪些状态保留到下一轮,哪些资源必须在本轮结束时清理。

每篇先给一个可以跟下去的例子,再展开源码、对象关系和失败情况。读完后,应当能从一个函数调用追到真正执行它的地方,并解释这一步依赖哪些前提。

阅读路线

  1. CPython 与 NumPy 怎样进入 Wasm:交叉编译、静态扩展注册与导入。
  2. Prefix:从源码片段到原位领取:完整前缀、提前读取、队首匹配和最终执行。
  3. PLM:完整 AST 的改写过程:从变换前后的代码观察依赖、控制流和异常位置。
  4. wazero Host 状态与 Guest 内存:编译代码、实例、调用栈、表和线性内存。
  5. COW 实现与快照复用条件:slice、地址范围、文件映射、写入私有化,以及安全复用的时机。
  6. 跨语言所有权与失败收尾:缓冲区、Python 引用、Future、取消和实际 defer 顺序。

最关心 COW 时,可以先读第四、第五篇,再回看构建和执行过程。

先分清三个观察层

Host 对象、Guest 线性内存与 Linux 页映射

上图展示 Linux COW 模式中的对象与映射关系。Guest 框内列出数据类别,位置不代表真实地址顺序。底部只画出一个未写入基线页的共享关系,新增匿名内存和已经私有化的页没有全部展开。点击图可单独放大。

阅读后面的 []byte 时,先问它描述哪一段存储。Go 的 slice 值记录数据起点、长度和容量;底下的字节可能由 Go 分配,也可能来自 Linux 的映射。在 COW 路径中,复制一个 slice 描述不会建立新的页映射。

再看 Guest 里的对象。CPython 的对象、字节缓冲和 C 栈放在线性内存中。wazero 自己的实例对象、函数引用和执行状态则还有各自的保存位置。一次内存复制能够恢复多少行为,要沿着这些引用逐项确认。

这组文章使用的实现版本

  • Spine:190ea2e238bba454cdcc335e95736c3f27f6be9a,本组写作开始时的代码版本。
  • 原始 Pysolate:702f6b6cf48b65f878b057d0c6bce22e852068ee,用于追踪已有的 NumPy 构建实现,只读检查。
  • wazero:Spine 的 go.mod 固定为 v1.11.0。

文中的当前运行结果会注明测试对象。源码片段用于解释具体路径,教学推演会说明输入和假设。没有执行过的构建步骤不会写成成功日志。

NumPy 的当前状态

本次在 Spine 现有 Wasm 产物中实际尝试导入 NumPy,结果为:

{"python":"3.14.0","error_type":"ModuleNotFoundError","error":"No module named 'numpy'"}

原始 Pysolate 则有独立的 numpy-core 构建 profile,包括 NumPy 源码选择、静态扩展 archive、初始化符号注册和包文件打包。第一篇会沿这条链解释 NumPy 怎样可被 CPython 导入,并列出 Spine 若要采用这条路径需要接上的部分。本轮保持 Spine 的运行时实现不变。

带着具体问题读

  • C 扩展被链接进 Wasm 后,Python 的 import 如何找到它?
  • 后面的工具先完成时,前面的赋值是否已经发生?
  • 同一个请求出现两次,准备队列怎样分别交付?
  • 对 A 的线性内存写一个字节,B 和基线分别会看到什么?
  • 放在 Go 对象里的数据,会不会随 Guest 内存一起恢复?
  • 取消发生后,哪段代码确保后台读取已经退出?

这些问题会在各篇中落实到具体数据和调用顺序。遇到 Go 组合写法不熟悉,可以回看 Go 组合速成;需要按文件逐段读时,可以回到 代码带读总路线。