RDMA基础概念:InfiniBand、RoCE、iWARP等协议栈,以及RDMA操作类型
核心原理:三种RDMA协议栈的由来与定位
RDMA(Remote Direct Memory Access)的核心思想是让网卡(RNIC,RDMA Network Interface Card)直接读写远端主机内存,绕过操作系统内核与CPU参与。但RDMA并非单一标准,而是由三种主流协议栈共同支撑,它们最大的区别在于物理层与传输层承载方式不同。
- InfiniBand(IB):从零设计的专用RDMA网络。物理层、链路层、网络层、传输层全部为RDMA优化,提供最低延迟(亚微秒级)与最高可靠性,但需要专用交换机、线缆和网卡,成本最高。IB网络中的节点通过子网管理器(Subnet Manager)进行集中式路径计算与地址分配。
- RoCE(RDMA over Converged Ethernet):在以太网上承载RDMA。RoCE v1在二层以太网帧中直接封装IB数据包(以太网类型0x8915),要求收发双方处于同一广播域;RoCE v2则将IB数据包封装进UDP/IPv4或IPv6报文(目标端口4791),实现三层路由,是目前数据中心最主流的方案。RoCE依赖无损以太网(PFC流控)或ECN拥塞控制来保证可靠性。
- iWARP(Internet Wide Area RDMA Protocol):在标准TCP/IP协议栈上实现RDMA。它利用TCP的可靠传输与拥塞控制,无需特殊交换机,兼容性最好,但延迟相对较高(因为TCP内核协议栈处理开销大),适合跨广域网或异构网络场景。
RDMA核心操作类型:从Verbs到硬件动作
RDMA操作通过Verbs API(如ibv_post_send)下发到网卡,网卡硬件直接解析并执行。所有操作都围绕队列对(QP,Queue Pair)展开——每个QP由发送队列(SQ)和接收队列(RQ)组成,配合完成队列(CQ)上报完成事件。理解操作类型是掌握RDMA编程的关键。
| 操作类型 | 方向 | 是否需要接收方预置缓冲区 | 典型用途 |
|---|---|---|---|
| Send / Recv | 双向 | 是(接收方必须提前post接收请求) | 传统消息传递、控制面交互 |
| RDMA Read | 单向(读远端) | 否(直接读取远端已注册内存) | 拉取远端数据、分布式存储读 |
| RDMA Write | 单向(写远端) | 否(直接写入远端已注册内存) | 推送数据、日志同步、参数更新 |
| RDMA Write with Immediate | 单向(写远端) | 否(但携带4字节立即数触发接收通知) | 数据+元数据一次性传递 |
| Compare & Swap(CAS) | 原子操作 | 否(对远端内存做原子比较交换) | 分布式锁、无锁数据结构 |
| Fetch & Add(FAA) | 原子操作 | 否(对远端内存做原子加) | 计数器、序列号生成 |
关键区别:Send/Recv是双边操作——接收方必须预先知道消息到来并post接收缓冲区,否则消息会被丢弃;而RDMA Read/Write是单边操作——只要远端内存已注册并暴露了RKey(远程访问密钥),发起方即可直接读写,接收方CPU完全无感知。单边操作是RDMA低延迟高吞吐的核心优势来源。
方法流程:一次典型RDMA Write的完整路径
以RoCE v2环境下的RDMA Write为例,展示从应用到硬件的完整流程。假设节点A要向节点B的某段内存写入数据。
// 节点B(接收方/被写方)初始化
ibv_reg_mr(pd, buf_b, size, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);
// 获取 buf_b 的 lkey/rkey,并通过带外通道(如TCP)告知节点A
// 节点A(发起方)执行RDMA Write
struct ibv_sge sge = { .addr = (uint64_t)local_buf, .length = size, .lkey = local_lkey };
struct ibv_send_wr wr = {
.opcode = IBV_WR_RDMA_WRITE,
.send_flags = IBV_SEND_SIGNALED,
.wr.rdma.remote_addr = remote_buf_addr, // 节点B的虚拟地址
.wr.rdma.rkey = remote_rkey // 节点B暴露的访问密钥
};
ibv_post_send(qp, &wr, &bad_wr);
硬件执行路径如下:
- 节点A的RNIC从SQ中取出WR,解析
remote_addr与rkey,将本地sge指向的数据切分为MTU大小的报文。 - 每个报文添加RoCE v2头(UDP+IP+IB BTH),通过以太网发出。
- 节点B的RNIC收到报文后,校验
rkey是否匹配该内存区域的权限,匹配则直接通过DMA将payload写入remote_addr指定的内存地址。 - 节点B的CPU全程不参与,不产生任何中断或系统调用。节点A在发送完成后,CQ中产生完成事件(如果设置了SIGNALED标志)。
IBV_WC_REMOTE_ACCESS_ERR错误。此外,rkey是安全凭证,泄露rkey等同于允许远端任意读写该内存区域。
案例场景:分布式KV存储中的操作选型
假设你在设计一个基于RDMA的分布式键值存储系统,不同操作应匹配不同RDMA原语:
- Get请求(读远端值):客户端使用RDMA Read直接读取服务端内存中的value区域。服务端只需在初始化时注册好内存池并共享rkey,客户端根据key计算出value的内存偏移后直接读取,服务端CPU零参与。
- Put请求(写远端值):客户端使用RDMA Write with Immediate将value写入服务端预分配的内存槽位,同时携带key作为立即数。服务端通过轮询CQ感知写入完成,并依据立即数更新索引结构。
- 原子计数器(如全局序列号):多个客户端并发对同一内存地址执行Fetch & Add,由网卡硬件保证原子性,避免锁竞争。
- 控制消息(如连接建立、心跳):使用Send/Recv双边操作,因为这类消息频率低、需要接收方主动处理。
该案例中,约80%的数据面流量走RDMA Read/Write单边操作,20%的控制面流量走Send/Recv,整体CPU利用率比传统TCP方案降低约60%,P99延迟从200μs降至15μs。
误区与复盘:协议栈选型与操作使用的常见陷阱
误区一:RoCE v2不需要任何网络调优。RoCE v2默认假设底层以太网不丢包。如果交换机未启用PFC(优先级流控)或ECN,一旦发生拥塞丢包,RoCE的Go-Back-N重传机制会导致性能断崖式下降(吞吐量可能下降90%以上)。部署RoCE前必须检查交换机buffer、PFC队列配置和ECN阈值。
误区二:iWARP因为走TCP所以一定慢。现代iWARP网卡(如Chelsio T6)将TCP卸载到硬件,实际延迟可接近RoCE。但若使用软件iWARP(如Soft-iWARP),则性能远不如RoCE。选型时要区分硬件卸载与软件模拟。
复盘建议:在项目初期,先用ibv_devinfo确认网卡支持的协议栈与原子操作能力;用ib_write_bw和ib_read_lat等perftest工具分别测试三种操作(Write/Read/Send)的带宽与延迟基线。很多性能问题在perftest阶段就能暴露,而不是等到应用集成后再排查。
最后需要强调的是,RDMA操作类型的选择并非越底层越好。RDMA Read虽然免去了接收方CPU参与,但会占用发起方的QP资源,且在大规模并发时可能引发接收端内存带宽瓶颈。实际工程中,往往需要结合消息大小、频率、一致性要求综合设计——小消息用Send/Recv或Write with Immediate,大块数据传输用Write/Read,需要原子语义时用CAS/FAA。理解每种操作的硬件行为与代价,才能在高性能网络应用中做出正确的架构决策。