同机零拷贝共享内存(SHM)
可用性:完整版(Full)专属。精简版(Tiny)无 SHM 能力,相关 API 在头文件中即被 声明剔除,误用在编译期即报错——这是一条明确的能力边界,而非运行期失败。
概述
OnePath 的「同机自动 SHM」让同一台机器上的不同 OnePath 会话之间自动经共享内存 零拷贝传输数据,无需用户手动管理内存池。开启后,用户继续使用普通的发布 / 订阅 API; 会话打开时 OnePath 自动探测对端是否在本机——同机走共享内存,跨机自动回退普通网络传输, 整个过程对用户完全透明。
开启方式
唯一入口是 onepath_config_enable_shm,在打开会话前的配置里调用一次:
onepath_config_t cfg;
onepath_config_new(&cfg);
onepath_config_set_mode(cfg, "peer");
onepath_config_enable_shm(cfg, NULL); /* 全默认:阈值 3072 字节,池 16 MB */
onepath_open_with_config(&session, cfg);
onepath_config_destroy(cfg);
/* 之后照常用普通发布 / 订阅 —— 同机的另一个会话收到的就是零拷贝 SHM */
onepath_publisher_put(pub, data, len);订阅端无需任何特殊处理:onepath_subscribe 收到的样本与普通传输完全一致。
阈值调优
onepath_shm_opts_t.message_size_threshold 控制「多大的消息用 SHM 零拷贝缓冲」:
| 设定 | 行为 | 适用 |
|---|---|---|
| 默认 3072 字节 | 小于此走普通缓冲,大于此零拷贝 | 通用负载(推荐) |
0 | 所有消息都零拷贝 | 大消息为主、想省每条拷贝时 |
阈值只决定缓冲类型,不决定是否经网络栈
阈值只决定单条消息是否走零拷贝缓冲,不改变同机会话间是否经过网络栈(只要开了 SHM, 传输链路就不经过网络栈)。阈值对「小包延迟」的影响在实测中并不稳定——延迟的主导项是 消息调度而非缓冲类型,因此默认 3072 通常是安全选择,无需为小包特意调低。
全零拷贝模式的内存池压力
threshold=0(全零拷贝)模式下,所有消息都从共享内存池分配。大消息(接近池容量)高频 发送时可能因内存池压力导致分配失败——大消息高频场景仍建议用默认 3072。若需更大池, 可在 onepath_shm_opts_t.pool_size 中调大。
onepath_shm_opts_t opts = { .message_size_threshold = 0 }; /* 全零拷贝 */
onepath_config_enable_shm(cfg, &opts);pool_size 设 0 用引擎默认 16 MB;数据量极大时可调大。
如何确认 SHM 生效
用 onepath_topology_local() 观察邻居链路的 is_shm 字段:同机邻居为 1,跨机为 0。 参见 网络拓扑感知。
onepath_topo_local_t topo;
if (onepath_topology_local(session, &topo) == ONEPATH_OK) {
for (size_t i = 0; i < topo.num_neighbors; i++)
printf("neighbor %s: shm=%d\n",
topo.neighbors[i].zid, topo.neighbors[i].is_shm);
onepath_topology_local_free(&topo);
}跨机行为
共享内存只在本机内有效。跨机会话(不同物理机 / 虚拟机)之间,同机探测失败,OnePath 自动回退到普通网络传输,通信照常进行,is_shm 如实报告为 0。无需为跨机部署做任何 特殊处理——同一份开启 SHM 的代码在单机和跨机环境下都正确工作。
性能特征
「同机自动 SHM」的价值在于大消息的零拷贝吞吐:消息不经过网络栈,省去内核态拷贝与 协议封装开销。需要澄清一个常见误解:
「不经网络栈 ⇒ 一定抗压低延迟」并不成立
SHM 传输的用户态缓冲管理(分配 / 回收)对 CPU 与内存带宽压力较为敏感。在系统被打满 (CPU + 内存带宽双重压力)时,SHM 的往返延迟可能与普通网络传输一样劣化,小包场景甚至 更明显。SHM 的价值是大消息零拷贝吞吐,而非抗网络拥塞的低延迟。 对小包延迟,无需为了 「走 SHM」而刻意调低阈值。
手动 SHM 池(高级通道)
除「同机自动 SHM」外,OnePath 另提供一组手动 SHM 池 API,从内存池显式分配零拷贝 缓冲。两者职责清晰、互不冲突:
| 同机自动 SHM | 手动 SHM 池 | |
|---|---|---|
| 开启 | 配置里一次 enable_shm | 每次发布传 pool |
| 用户 API | 普通发布 / 订阅 | put_shm 专用 |
| 覆盖范围 | 同机 ≥ 阈值消息 | 任意大小消息(强制零拷贝) |
| 适用 | 绝大多数生产场景 | 极致延迟 / 吞吐基准 |
通常只需同机自动 SHM。手动池保留为「对任意小消息强制确定性零拷贝」的高级通道。
手动 SHM API 参考
int onepath_shm_pool_create(onepath_session_t s, onepath_shm_pool_t *out, size_t pool_size);
int onepath_publisher_put_shm(onepath_publisher_t pub, onepath_shm_pool_t pool,
const void *data, size_t len);
int onepath_put_shm(onepath_session_t s, const char *key, onepath_shm_pool_t pool,
const void *data, size_t len);
void onepath_shm_pool_destroy(onepath_shm_pool_t pool);| 函数 | 说明 |
|---|---|
onepath_shm_pool_create(s, &out, pool_size) | 创建共享内存池,用于零拷贝发布。需先在配置中 onepath_config_enable_shm。 |
onepath_publisher_put_shm(pub, pool, data, len) | 经共享内存发布:数据先拷贝到 SHM 池,再走零拷贝路径发送。 |
onepath_put_shm(s, key, pool, data, len) | 一次性经共享内存发布(无需先声明发布者)。 |
onepath_shm_pool_destroy(pool) | 销毁内存池;确保所有使用该池的发布已完成后再销毁。 |
配置选项 onepath_shm_opts_t
typedef struct {
size_t message_size_threshold; /* 触发 SHM 的最小消息字节; 0 = 所有消息都走 SHM */
size_t pool_size; /* SHM 池字节; 0 = 引擎默认 16 MB */
} onepath_shm_opts_t;| 字段 | 含义 | 默认 |
|---|---|---|
message_size_threshold | 触发零拷贝缓冲的最小消息字节;0 = 所有消息都走 SHM | 3072 |
pool_size | SHM 池字节;0 = 引擎默认 | 16 MB |
onepath_config_enable_shm 返回 ONEPATH_OK 成功,ONEPATH_ERR_PARAM(cfg 为空), ONEPATH_ERR(写入失败)。
创建 / 销毁配对
| 创建 | 销毁 |
|---|---|
onepath_shm_pool_create | onepath_shm_pool_destroy |
更多内存与所有权规则见 内存管理。