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内核协议栈处理开销大),适合跨广域网或异构网络场景。
选型直觉:追求极致性能且预算充足选IB;在现有以太网基础设施上获得低延迟选RoCE v2;需要跨三层路由或广域网传输且无法改造网络设备时选iWARP。
RDMA协议栈分层对比 应用层(Verbs API / libibverbs / 用户态应用) InfiniBand IB传输层(RC/UC/UD) IB网络层(全局路由GRH) 链路层:IB专用(LRH) RoCE v2 UDP封装(端口4791) IP网络层(IPv4/IPv6) 以太网链路层(需无损以太网) iWARP DDP / MPA 映射层 TCP传输层(可靠字节流) IP网络层 / 以太网链路层 物理层:IB专用线缆 / 以太网双绞线或光纤 关键特性对比 延迟: IB < 1μs RoCE v2 ≈ 1-2μs iWARP ≈ 5-10μs 交换机要求: 专用IB交换机 支持PFC/ECN的以太网交换机 标准以太网交换机即可

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);

硬件执行路径如下:

  1. 节点A的RNIC从SQ中取出WR,解析remote_addr与rkey,将本地sge指向的数据切分为MTU大小的报文。
  2. 每个报文添加RoCE v2头(UDP+IP+IB BTH),通过以太网发出。
  3. 节点B的RNIC收到报文后,校验rkey是否匹配该内存区域的权限,匹配则直接通过DMA将payload写入remote_addr指定的内存地址。
  4. 节点B的CPU全程不参与,不产生任何中断或系统调用。节点A在发送完成后,CQ中产生完成事件(如果设置了SIGNALED标志)。
常见误区:RDMA Write不需要接收方post接收请求,但必须提前完成内存注册(ibv_reg_mr)并共享rkey。很多初学者误以为“单边操作=无需任何准备”,导致实际运行时收到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。理解每种操作的硬件行为与代价,才能在高性能网络应用中做出正确的架构决策。