Skip to content

同机零拷贝共享内存(SHM)

可用性:完整版(Full)专属。精简版(Tiny)无 SHM 能力,相关 API 在头文件中即被 声明剔除,误用在编译期即报错——这是一条明确的能力边界,而非运行期失败。

概述

OnePath 的「同机自动 SHM」让同一台机器上的不同 OnePath 会话之间自动经共享内存 零拷贝传输数据,无需用户手动管理内存池。开启后,用户继续使用普通的发布 / 订阅 API; 会话打开时 OnePath 自动探测对端是否在本机——同机走共享内存,跨机自动回退普通网络传输, 整个过程对用户完全透明。

开启方式

唯一入口是 onepath_config_enable_shm,在打开会话前的配置里调用一次:

c
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 中调大。

c
onepath_shm_opts_t opts = { .message_size_threshold = 0 };  /* 全零拷贝 */
onepath_config_enable_shm(cfg, &opts);

pool_size0 用引擎默认 16 MB;数据量极大时可调大。

如何确认 SHM 生效

onepath_topology_local() 观察邻居链路的 is_shm 字段:同机邻居为 1,跨机为 0。 参见 网络拓扑感知

c
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 参考

c
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

c
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 = 所有消息都走 SHM3072
pool_sizeSHM 池字节;0 = 引擎默认16 MB

onepath_config_enable_shm 返回 ONEPATH_OK 成功,ONEPATH_ERR_PARAM(cfg 为空), ONEPATH_ERR(写入失败)。

创建 / 销毁配对

创建销毁
onepath_shm_pool_createonepath_shm_pool_destroy

更多内存与所有权规则见 内存管理

OnePath™ 以预构建库形式交付,运行时零外部依赖。