CubeSandbox ivshmem 共享内存实战指南:模板启用、设备定位与环形缓冲协议构建
【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox
本指南以 CubeSandbox 仓库中examples/ivshmem目录的官方示例(README.md)为主体,完整讲解如何在 CubeSandbox 中为模板启用ivshmem可选的主机/客户机共享内存通道:从模板构建时的开关配置,到宿主机与客户机两侧共享内存区域的呈现方式,再到基于双环形缓冲区构建自定义通信协议与宿主机侧 mmap 基准测试。读完本文,你将能够独立完成「启用 ivshmem 模板 → 创建沙箱 → 定位共享内存 → 自定义协议通信 → 性能摸底」的完整闭环。
1. 背景:为什么需要 ivshmem
CubeSandbox 是一个面向 AI Agent 的即时、并发、安全且轻量的沙箱系统,每个沙箱本质上是一个由自研 Rust 虚拟化平台(仓库hypervisor/目录)承载的轻量虚拟机。在大多数场景下,主机与客户机之间通过网络栈(CubeProxy、CubeVS)通信即可满足需求;但当应用需要极低延迟、可自定义内存布局的数据通道时,通用网络路径并非最优解。
此时ivshmem(Inter-VM Shared Memory,虚拟机间共享内存)提供了一个模板级的可选通道:
- 它以模板(Template)为粒度开启(
enableIvshmem=true),而不是全局开关,因此只有真正需要共享内存的工作负载才会承担相应资源; - 每个由该模板创建的沙箱,在宿主机侧拥有一个独立的共享内存后端文件
/dev/shm/ivshmem-{sandbox_id}; - 在客户机侧,同一块内存区域以
ivshmemPCI 设备的形式暴露,客户机进程可以通过打开 BAR 资源文件并mmap()来与宿主机交换数据。
从仓库源码可以印证这一架构:自研虚拟化平台hypervisor/中实现了完整的 ivshmem PCI 设备(hypervisor/devices/src/ivshmem.rs),其厂商号0x1af4、设备号0x1110、共享内存 BAR 索引等关键常量均定义于此(见第 22–29 行)。虚拟化平台还提供了--ivshmem path=<backend>,size=<size>的启动参数解析与校验逻辑(hypervisor/vmm/src/config.rs 第 2202–2234 行),要求后端文件大小必须为 2 的幂,默认大小由DEFAULT_IVSHMEM_SIZE = 128(MiB)决定(hypervisor/vmm/src/vm_config.rs 第 530–545 行)。
正因如此,ivshmem非常适合构建自定义数据通路:你可以完全自主地定义主机与客户机之间的内存布局、环形缓冲协议或其他共享结构,而不受网络协议栈的约束。
2. 前置条件
开始前请确认以下条件:
- 一个已运行可用的 CubeSandbox 部署(含 CubeAPI、CubeMaster、Cubelet 等核心组件);
- Python 3.8 及以上版本;
- 一个可用于构建模板的容器镜像(如仓库部署脚本使用的
sandbox-code:latest一类镜像)。
安装 Python SDK 依赖(cubesandbox包,版本不低于 0.5.0):
pip install "cubesandbox>=0.5.0"或者,如果希望在本地仓库环境内使用辅助依赖文件,也可以直接从 code-sandbox-quickstart 示例安装:
pip install -r examples/code-sandbox-quickstart/requirements.txt该文件内容可在仓库中查看:examples/code-sandbox-quickstart/requirements.txt。
3. 快速开始:三步跑通共享内存
Step 1 — 构建启用 ivshmem 的模板
方式一:Python SDK
from cubesandbox import Template job = Template.build( image="cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest", enable_ivshmem=True, ) template_id = job.template_id注意:Template.build()提交的是 CubeAPI 的 create-from-image 异步任务,返回的TemplateBuild对象包含job_id(即build_id)、template_id与状态字段;需要轮询Template.get_build_status(template_id, build_id)或Template.get(template_id)等待构建完成。该接口的完整签名与参数校验逻辑可参考 SDK 源码 sdk/python/cubesandbox/_template.py(enable_ivshmem参数会原样透传为请求体中的enableIvshmem字段,见第 338–339 行)。
方式二:cubemastercli 命令行
cubemastercli tpl create-from-image \ --image cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest \ --enable-ivshmem--enable-ivshmem标志在 cubemastercli 中的解析逻辑位于 CubeMaster/cmd/cubemastercli/commands/cubebox/template.go(第 184–190 行与第 890 行的标志定义),它会把布尔值写入CreateTemplateFromImageReq.EnableIvshmem字段。该字段在 CubeMaster/pkg/service/sandbox/types/types.go 中定义(EnableIvshmem *bool,json 标签为enable_ivshmem),并最终映射到沙箱注解cube.master.enable_ivshmem(CubeMaster/pkg/base/constants/constants.go 第 124 行)。
命令成功后请记录输出的template_id,后续创建沙箱时会用到。
Step 2 — 运行端到端环形缓冲 Demo
创建临时沙箱并运行完整的端到端示例脚本:
python examples/ivshmem/ivshmem_ring_demo.py \ --template <ivshmem_template_id> \ --message "ping from host" \ --cleanup预期输出形如:
sandbox_id: ... host shm: /dev/shm/ivshmem-... host sent: ping from host guest received: ping from host host recv: hello-from-guest: ping from host这条输出序列完整展示了数据通路:宿主机把消息写入 host→guest 环形缓冲,客户机从resource2读取并在 guest→host 环形缓冲中写入应答,宿主机再从共享内存后端文件读回应答。脚本源码见 examples/ivshmem/ivshmem_ring_demo.py。
Step 3 — 运行宿主机侧 mmap 基准测试
python examples/ivshmem/ivshmem_benchmark.py \ --template <ivshmem_template_id> \ --count 3 \ --cleanup该基准测试仅针对宿主机侧/dev/shm/ivshmem-{sandbox_id}后端文件的 mmap 访问,不涉及客户机协议。它适用于反复创建多个沙箱来检验宿主机侧后端行为的一致性,也支持--parallel参数并行跑多个沙箱做横向对比(详见第 5 节)。脚本源码见 examples/ivshmem/ivshmem_benchmark.py。
4. 共享内存在两侧如何呈现
理解 ivshmem 的关键在于「同一块内存、两个视图」:宿主机看到的是普通文件,客户机看到的是 PCI 设备。
4.1 宿主机侧:一个共享内存文件
每个启用 ivshmem 的沙箱,在宿主机上都会有一个对应的后端文件:
/dev/shm/ivshmem-{sandbox_id}宿主机可以像操作普通文件一样打开并映射它。由于文件名直接以沙箱 ID 命名,示例脚本中专门封装了wait_for_shm_file()(轮询等待文件出现,默认超时 60 秒,见 ivshmem_ring_demo.py 第 42–49 行)来处理沙箱创建与后端文件就绪之间的时间差。
最小写入示例:
import mmap shm_path = f"/dev/shm/ivshmem-{sandbox_id}" with open(shm_path, "r+b", buffering=0) as f: mm = mmap.mmap(f.fileno(), 1024 * 1024) mm[:16] = b"hello-from-host" mm.flush() mm.close()4.2 客户机侧:一个 ivshmem PCI 设备
客户机(虚拟机)内看到的则是一个ivshmemPCI 设备,厂商号/设备号为0x1af4/0x1110,共享内存 BAR 暴露为resource2。这段常量与 BAR 布局在虚拟化平台源码中有明确对应:IVSHMEM_VENDOR_ID = 0x1af4、IVSHMEM_DEVICE_ID = 0x1110、IVSHMEM_BAR2_IDX = 2(hypervisor/devices/src/ivshmem.rs 第 22–29 行)。
下面的代码在/sys/bus/pci/devices下遍历 PCI 设备,按厂商/设备号定位 ivshmem 设备并映射resource2:
import mmap import os resource = None for name in os.listdir("/sys/bus/pci/devices"): d = f"/sys/bus/pci/devices/{name}" try: vendor = open(f"{d}/vendor").read().strip() device = open(f"{d}/device").read().strip() except OSError: continue if vendor == "0x1af4" and device == "0x1110": resource = f"{d}/resource2" break if resource is None: raise RuntimeError("ivshmem PCI device not found") with open(resource, "r+b", buffering=0) as f: mm = mmap.mmap(f.fileno(), 1024 * 1024) print(bytes(mm[:16])) mm.close()mmap()的映射长度必须与后端共享内存区域大小一致(示例中为 1 MiB,即IVSHMEM_SIZE = 1024 * 1024)。从虚拟化平台源码看,ivshmem 后端文件的默认大小是 128 MiB(DEFAULT_IVSHMEM_SIZE = 128,见 hypervisor/vmm/src/vm_config.rs 第 530 行),且要求大小为 2 的幂(hypervisor/vmm/src/config.rs 第 2220–2227 行),因此示例中映射 1 MiB 只是使用共享区域的前段,完整区域大小由虚拟化层决定。
5. 两个示例脚本的设计解析
5.1ivshmem_ring_demo.py:最小共享内存协议
这个脚本演示了应用代码可采用的极简协议形态,其数据流为:
- 一个 host → guest 方向的环形缓冲(ring buffer);
- 一个 guest → host 方向的环形缓冲;
- 宿主机写入一条消息;
- 客户机从
resource2读取该消息; - 客户机写入一条应答消息;
- 宿主机从
/dev/shm/ivshmem-{sandbox_id}读回应答。
其内存布局在 ivshmem_ring_demo.py 中通过一组常量精确定义(第 32–39 行):
| 常量 | 值 | 含义 |
|---|---|---|
IVSHMEM_SIZE | 1024 * 1024 | 映射的共享内存区域大小(1 MiB) |
RING_SLOT_COUNT | 8 | 每个环形缓冲的槽位数量 |
RING_SLOT_DATA_SIZE | 256 | 单个槽位的数据区大小(字节) |
RING_HEADER_FORMAT | "<IIII" | 环形头布局:head、tail、slot_count、slot_data_size |
RING_SLOT_SIZE | 4 + RING_SLOT_DATA_SIZE | 单个槽位总大小(4 字节长度前缀 + 数据区) |
HOST_TO_GUEST_OFFSET | 0 | host→guest 环在共享区中的起始偏移 |
GUEST_TO_HOST_OFFSET | 64 * 1024 | guest→host 环在共享区中的起始偏移 |
环形缓冲的读写基于「头尾指针」模型实现:ring_send()检查tail - head < RING_SLOT_COUNT判断是否有空位,写入长度前缀与负载后推进tail;ring_recv()检查tail > head判断是否有数据,读出后推进head(第 135–165 行)。每个环的头部用一个 16 字节的"<IIII"结构记录 head、tail、槽位总数与槽位数据大小,槽位内前 4 字节是负载长度,后续为负载数据。
客户机侧的协议逻辑以 Python 代码字符串的形式由宿主机通过sandbox.commands.run("python3 - <<'PY' ... PY")注入执行(第 168–253 行的guest_script()),它复用完全相同的布局常量与读写函数:先定位 ivshmem PCI 设备(vendor0x1af4/ device0x1110),映射resource2,从HOST_TO_GUEST_OFFSET环收消息,向GUEST_TO_HOST_OFFSET环发应答。示例脚本刻意保持客户机侧用 Python 实现以追求可读性。
使用该示例时,你可以重点观察:
- 如何创建或连接沙箱(
Sandbox.create/Sandbox.connect,见第 98–103 行); - 如何等待宿主机共享内存文件出现(
wait_for_shm_file); - 如何在两侧映射同一块内存;
- 如何定义偏移量与槽位布局;
- 如何通过小型共享内存协议交换消息。
5.2ivshmem_benchmark.py:宿主机侧 mmap 基准
这个脚本只关注宿主机侧后端文件,不运行任何客户机协议。它使用IvshmemBenchmark类(ivshmem_benchmark.py 第 28–67 行)对共享内存文件执行四类写测试:
| 测试项 | 内容 | 度量 |
|---|---|---|
single_byte | 单字节写入iterations次 | 单字节延迟(µs/op)与吞吐(ops/s) |
block_100b | 100 字节块写入 | 延迟与吞吐(MB/s) |
block_1kb | 1 KiB 块写入 | 延迟与吞吐(MB/s) |
block_100kb | 100 KiB 块写入(迭代数自动降至 min(iterations, 1000)) | 延迟与吞吐(MB/s) |
主要命令行参数:
--template:ivshmem 模板 ID,用于批量创建临时沙箱;--sandbox-id:直接对已存在的沙箱做基准(与--template二选一);--count:要创建的沙箱数量(默认 1);--iterations:每个测试的迭代次数(默认 10000);--parallel:对多个沙箱并行执行基准测试;--cleanup:结束后删除临时沙箱。
当--count大于 1 时,脚本会输出多沙箱汇总(print_summary,第 125–142 行),包括平均单字节延迟、平均 1 KiB 吞吐与平均 100 KiB 吞吐,便于横向比较宿主机后端在不同沙箱间的一致性。
需要说明:基准测试的数字是示例自带的可执行观测手段,具体数值会随宿主机硬件、
/dev/shm挂载方式与沙箱负载变化,本文不提供任何固定性能结论;请在你自己的环境中运行以获取真实数据。
6. 什么时候应该在此基础上构建
这两个示例刻意保持小巧、可读,是构建自定义共享内存数据通路的起点。适合在其上发展的场景包括:
- 请求-应答型控制消息(request-reply control messages);
- 主机与客户机之间的环形缓冲流式传输(ring-buffered streaming);
- 共享的描述符、索引或帧元数据(shared descriptors, indexes, or frame metadata);
- 自定义零拷贝应用协议(custom zero-copy application protocols)。
需要特别指出的是:示例中的客户机逻辑使用 Python 仅为演示协议形态。对于生产环境中的客户机侧热路径(hot path),通常应把客户机逻辑迁移到开销更低的实现(如 C、C++ 或 Rust),并自行设计同步策略(例如基于 ivshmem BAR0 中的门铃寄存器与中断机制,或自选的自旋锁/原子操作方案)。虚拟化平台实现的 ivshmem 设备还支持 BAR0 中的IVPosition与Doorbell寄存器(见 hypervisor/devices/src/ivshmem.rs 第 31–53 行的寄存器布局注释),可为更复杂的多对等同步方案提供底层支撑。
7. 故障排查
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
/dev/shm/ivshmem-{sandbox_id}未出现 | 模板构建时未开启enableIvshmem=true | 重新以 ivshmem 模式构建模板(见第 3 节 Step 1) |
客户机找不到0x1af4/0x1110设备 | 沙箱由非 ivshmem 模板创建 | 确认沙箱对应的模板 ID 是否为 ivshmem 模板 |
resource2缺失 | 客户机未按预期暴露 ivshmem BAR | 检查/sys/bus/pci/devices/*下的 PCI 设备 |
| 环形 Demo 超时 | 主机与客户机使用的布局或偏移不一致 | 核对环偏移、槽位大小与负载长度假设是否一致 |
除表格外,还有几个示例脚本自带的实用排查手段:环 Demo 默认等待共享内存文件 60 秒(wait_for_shm_file,可通过--timeout调节沙箱等待)、沙箱就绪探测通过sb.commands.run("echo ready")轮询完成(wait_cmd,第 83–95 行);两个脚本都支持--sandbox-id复用已有沙箱做单点调试,避免反复创建。脚本还内置了若干防御性检查:ring_send会拒绝超过RING_SLOT_DATA_SIZE(256 字节)的负载(第 136–137 行),从而把「负载超长」类问题直接暴露为明确的ValueError而非协议错乱。
8. 进一步阅读
- examples/snapshot-rollback-clone:面向生命周期管理的 Python SDK 示例(快照、回滚、克隆);
- examples/code-sandbox-quickstart:最基础的 SDK 使用流程;
- SDK 源码:sdk/python/cubesandbox,其中 sdk/python/cubesandbox/_template.py 的
Template.build()是启用 ivshmem 的入口; - 虚拟化平台侧的 ivshmem 设备实现:hypervisor/devices/src/ivshmem.rs,以及启动参数解析:hypervisor/vmm/src/config.rs;
- SDK 中
enable_ivshmem透传行为的单元测试:sdk/python/tests/test_sandbox.py(test_build_forwards_create_from_image_options断言请求体中包含"enableIvshmem": True)。
【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考