目录

面向共享内存的多路径 RPC 系统(CXL-RPC)

项目信息 内容
比赛 中国研究生操作系统开源创新大赛
作品名称 面向共享内存的多路径 RPC 系统
参赛队伍 STARs
验证平台 openEuler 24.03 LTS SP3

基于跨机共享内存的统一 RPC 运行时原型,使用 C++17 实现。项目在两台 QEMU/KVM Guest 之间通过 ivshmem 验证共享内存协议,同时提供 TCP Transport。同一套 RPC 接口支持 SHM_ONLYTCP_ONLYAUTOSTRIPE 四种调用模式,实际数据由 SHM Inline、SHM Buffer 或 TCP 三种路径承载。

项目当前重点不是宣称在普通设备上取得物理 CXL 的极限性能,而是验证:

  • 固定布局、无指针的跨进程共享内存协议;
  • 多级优先调度、背压与有界资源管理;
  • 大对象的 Chunk 生命周期与最终回收;
  • SHM/TCP 统一 API、确定性自适应选路和请求级 STRIPE;
  • 超时、故障切换、旧会话隔离和禁止非幂等请求重复执行;
  • 复杂结构体在不同传输路径上的一致编解码。

快速验收

# Release 构建与完整回归
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure

# 复杂类型 KV 与统一多路径演示
./build/kv_rpc_demo

# AUTO 确定性决策回放
./build/auto_decision_replay_test

# 异常链路与资源回收演示
./scripts/run_fault_injection_demo.sh build

验收时建议首先运行 kv_rpc_demo。它会逐项展示方法名、请求/返回大小、 实际路径、选路原因、状态、request_id 和回收结果,并在结束时确认 Upload/Download Chunk 均归零。

核心能力

能力 当前实现
共享内存基础 POSIX SHM 本地后端、ivshmem /dev/mem 物理映射后端
请求与响应 固定容量 SPSC Submit/Completion Ring,per-slot sequence
服务端并发 Progress、Scheduler、Worker Pool、Completion Publisher
多级优先调度 High/Normal/Low 独立 Ring、加权服务、Head Aging、High Chunk 保留
小对象 不超过 192 B 的请求/响应走 SHM Inline
大对象 64 KiB Chunk、连续 extent、Upload/Download 双 Pool、自动 RELEASE
网络路径 TCP Transport 与 SHM 共用 Completion 和 Future 语义
AUTO 基于请求特征、路径健康和资源压力的确定性自适应选路
STRIPE 请求级加权分流,发布前允许切换,发布后不跨路径重放
复杂类型 KV PUT、GET、DELETE、LIST,支持嵌套结构、数组和二进制安全字符串
异常处理 流控、故障降级、超时迟到回收、重复 RELEASE、Session epoch 隔离

总体架构

alt text

客户端只面对统一的同步/异步 RPC API。SHM 和 TCP 的完成结果最终进入 同一套 request ID 匹配、超时和 Future 处理流程。服务端 Worker 不直接 写共享 Completion Ring,而是经有界 Completion Outbox 交给唯一 Publisher。

传输路径与选路模式

模式 作用 典型场景
SHM_INLINE 载荷直接放入 256 B Descriptor 的 Inline 区 小请求、小响应
SHM_BUFFER Descriptor 传递 BufferHandle,数据位于 Chunk Pool 大请求或大响应
TCP TCP Frame 传输,复用统一 Completion SHM 不可用或显式网络调用
AUTO 按确定性准入规则选择 SHM/TCP 健康切换与资源压力降级
STRIPE 按权重在可用路径间做请求级分流 双路径并行使用

AUTO 与 STRIPE 的 fallback 只发生在物理发布之前。请求一旦进入 SHM Ring 或写入 TCP,就不会因为超时而在另一条路径重新执行,从而避免 PUT、 DELETE 等非幂等操作执行两次。

多级优先调度

服务端为 High、Normal、Low 配置独立 Submit Ring,并使用确定性加权调度:

  • 默认权重为 3:2:1
  • 每个调度窗口限制批量,避免单一优先级长期占用 Worker;
  • Normal/Low 等待超过阈值时由 Head Aging 提升,防止饥饿;
  • 本地 Task Queue、Worker Inbox、Completion Outbox 全部有界并记录背压;
  • High 优先级保留一部分 Chunk,Normal/Low 不得消耗该保留量;
  • 多 Worker 允许乱序完成,客户端按 request_id 精确匹配。

确定性专项验证得到默认 3:2:1 -> 30:20:10、自定义 1:1:2 -> 10:10:20;Low 权重为 0 时仍可通过 Aging 得到服务。

快速构建与测试

依赖 Linux、CMake、支持 C++17 的 GCC/G++ 和 pthread:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure

当前完整测试集为 31 项,覆盖共享内存、跨进程原子、SPSC Ring、Inline、 Worker Pool、优先调度、Chunk Allocator、TCP、AUTO、STRIPE、KV Wire、 超时、Session restart 和资源回收。

复杂类型 KV RPC Demo

数据模型

struct UserProfile {
    uint64_t id;
    std::string name;
    std::vector<int32_t> scores;
    Metadata metadata;
};

内存 KV 提供:

  • PUT(key, UserProfile)
  • GET(key) -> UserProfile
  • DELETE(key)
  • LIST(prefix) -> vector<Entry>

KV Wire v1 使用固定宽度整数和大端字节序;字符串、数组采用 length + data;Envelope、UserProfileMetadata 均带版本。 解码器拒绝超长、截断、伪造长度、计数溢出、尾随数据和未知版本,线上 不会传递进程内指针、size_t 或 STL 对象布局。

一条命令演示

./build/kv_rpc_demo

关键输出:

SCENARIO         METHOD      REQ(B)    RESP(B)  ACTUAL_PATH DECISION_REASON       STATUS  REQUEST_ID  RECLAIMED
small profile    PUT             91         12  SHM_INLINE  SMALL_MESSAGE         OK               1  YES
small profile    GET             26         77  SHM_INLINE  SMALL_MESSAGE         OK               2  YES
large profile    PUT          81997         12  SHM_BUFFER  SHM_BUFFER_AVAILABLE  OK               3  YES
large profile    GET             26      81983  SHM_BUFFER  SMALL_MESSAGE         OK               4  YES
SHM marked down  PUT             94         12  TCP         SHM_DOWN              OK               5  YES
SHM recovered    GET             29         77  SHM_INLINE  SMALL_MESSAGE         OK               6  YES

final upload_chunks=0
final download_chunks=0
kv_rpc_demo: PASS paths=inline,buffer,tcp,auto,stripe shm_tcp_equal=1 release_acks=4 chunks=0

GET 的请求本身很小,因此 AUTO 的请求准入原因仍可能是 SMALL_MESSAGE; 当返回的 UserProfile 超过 192 B 时,服务端实际响应路径会自动变为 SHM Buffer。

AUTO 确定性自适应选路

AUTO 不做性能训练,也不承诺预测实时“绝对最优路径”。规则按固定顺序 结合请求特征、路径健康和资源压力执行:

条件 决策
小请求且 SHM 可接纳 SHM Inline
大请求且存在足够连续 Chunk SHM Buffer
Ring 达到高水位、迟滞区间或无空槽 TCP(若健康)
Completion 空间不足或 SHM outstanding 已满 TCP(若健康)
SHM down、未 READY 或处于恢复冷却 TCP(若健康)
Normal/Low 会消耗 High 保留 Chunk TCP(若健康)
TCP down 且 SHM 仍可接纳 保持 SHM
两条路径均不可用 本地立即失败,不发布请求

决策回放不依赖机器性能,可用虚拟资源快照复现同一结果:

./build/auto_decision_replay_test
auto_decision_replay_test: PASS cases=10 inline=1 buffer=1 ring=1 completion=1 fragment=1 reservation=1 shm_down=1 tcp_down=1 no_path=1 deterministic=1

故障注入 Demo

./scripts/run_fault_injection_demo.sh build

该命令运行真实集成测试并汇总以下异常链路:

故障 预期行为
SHM down 发布前自动切换 TCP
TCP down 保持使用 SHM
Request Ring 高水位或满 流控或发布前转 TCP
Pool 存在空闲但连续 extent 不足 发布前转 TCP
High 优先级 Chunk 保留 Normal/Low 不侵占保留量
超时后到达 Completion tombstone 接管并最终 RELEASE
重复 RELEASE 返回 ALREADY_RELEASED
Session restart 后旧响应 按旧 epoch 拒绝或忽略
发布后超时 不跨路径重放

通过条件为:

fault_injection_demo: PASS scenarios=9

专项测试

# KV Wire、KV RPC、AUTO 回放
ctest --test-dir build \
  -R 'kv_(wire|rpc)_test|auto_decision_replay_test' \
  --output-on-failure

# 多级优先调度
./build/priority_scheduler_test

# STRIPE 选择、故障切换与并发
ctest --test-dir build -R 'stripe_.*_test' --output-on-failure

# SHM Buffer 生命周期
ctest --test-dir build \
  -R 'chunk_allocator|shm_buffer' \
  --output-on-failure

复杂类型测试覆盖空字符串、中文、嵌入 \0、空/普通/大数组、嵌套结构、 191/192/193 B、64 KiB Chunk、精确 1 MiB、截断 Payload、伪造长度、 整数溢出、未知版本,以及 SHM/TCP 字节一致性和超时后的最终回收。

openEuler 双 Guest 验证

当前功能源码已在两台 openEuler 24.03 LTS SP3 Guest 上分别执行 Release 构建和全量测试:

验证项 结果
VM1 / VM2 Release 构建 PASS / PASS
VM1 / VM2 CTest 31/31 / 31/31
KV / AUTO / 故障 Demo PASS
双向 ivshmem 原子探针 各 100,000 轮,errors=0
跨 Guest SHM Buffer 65,537 B,100 轮
Buffer 生命周期 300 个 Wire Request,100 个 RELEASE ACK
最终 Chunk upload_chunks=0download_chunks=0

KV Demo 是 VM 内自包含业务演示;跨 Guest 复验覆盖同一份源码的原子可见性 和 SHM Buffer/RELEASE 主链。目前不声称 KV 的独立 Client/Server 业务已经 跨两台 Guest 运行,也不把 QEMU/ivshmem 或 loopback TCP 数据描述为物理 CXL 硬件性能。

完整环境与关键输出见 docs/results/

底层验证工具

这些工具用于验证共享内存基础,不是推荐的答辩入口。

布局检查

./build/layout_check

POSIX SHM 原子可见性

先启动负责创建并初始化 POSIX SHM 对象的 Producer:

./build/shm_atomic_probe --role producer --initialize --backend posix \
  --name /cxlrpc-probe --size 131072 --create --truncate

再在另一个终端启动 Consumer:

./build/shm_atomic_probe --role consumer --backend posix \
  --name /cxlrpc-probe --size 131072

ADD Inline RPC

Client 负责初始化共享区,Server 使用相同映射接入:

./build/add_rpc_demo --role client --initialize --backend posix \
  --name /cxlrpc-add --size 2097152 --iterations 100000

./build/add_rpc_demo --role server --backend posix \
  --name /cxlrpc-add --size 2097152 --iterations 100000

在双 Guest 环境中将 --backend posix 替换为 ivshmem /dev/mem 映射参数, 并在运行前确认 PCI BAR 的物理地址和大小。

代码结构

include/cxlrpc/   公共 API、ABI、Wire 类型与策略定义
src/memory/       共享内存映射、Chunk Allocator
src/runtime/      RPC Runtime、服务端、KV Wire 与统一客户端
src/transport/    SHM/TCP Transport
tests/            单元、集成、跨进程与故障测试
tools/            KV、ADD、Buffer、布局和原子探针 Demo
scripts/          故障注入与环境辅助脚本
docs/             技术规范和最终验证结果

技术文档

关于
44.3 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号