3. RDMA核心原理与协议栈

RDMA(Remote Direct Memory Access)的本质,是让一台主机在不打扰对端 CPU 的前提下,直接读写远端进程虚拟地址空间中的数据。要做到这一点,仅靠“网卡支持 RDMA”远远不够,还需要一整套用户态语义、内存管理机制和链路层协议协同工作。本节围绕 Verbs 编程接口、队列对(QP)、完成队列(CQ)、内存注册(MR)以及 RoCEv2 与 InfiniBand 的差异展开,帮助读者建立从“应用调用”到“网卡发包”的完整链路认知。

一、Verbs:RDMA 的用户态编程契约

Verbs 是应用程序访问 RDMA 硬件的标准接口,由 OpenFabrics 组织定义,libibverbs 是其用户态实现。它把“创建资源、注册内存、提交工作请求、收割完成事件”抽象成一组函数调用。典型流程如下:

// 1. 获取设备列表并打开设备
ibv_devices();  ibv_get_device_list(&num);
ctx = ibv_open_device(dev);

// 2. 查询端口属性,获取 LID/GID
ibv_query_port(ctx, 1, &port_attr);

// 3. 分配保护域 PD
pd = ibv_alloc_pd(ctx);

// 4. 注册内存区域 MR,得到 lkey/rkey
mr = ibv_reg_mr(pd, buf, size,
        IBV_ACCESS_LOCAL_WRITE |
        IBV_ACCESS_REMOTE_READ |
        IBV_ACCESS_REMOTE_WRITE);

// 5. 创建完成队列 CQ
cq = ibv_create_cq(ctx, cqe_num, NULL, NULL, 0);

// 6. 创建队列对 QP,并绑定 CQ
qp = ibv_create_qp(pd, &qp_init_attr);

// 7. 状态迁移:INIT → RTR → RTS
// 8. 提交 WR(ibv_post_send / ibv_post_recv)
// 9. 轮询 CQ(ibv_poll_cq)获取完成

这段代码揭示了 Verbs 的核心思想:资源先建、内存先注册、状态先迁移、请求后提交、完成再收割。任何一步缺失,后续操作都会失败。例如未注册的内存不能作为 RDMA 缓冲区,QP 未进入 RTS 状态则无法收发数据。

二、队列对 QP:RDMA 通信的基本单元

队列对由发送队列(SQ)和接收队列(RQ)组成,是 RDMA 通信的最小逻辑通道。每个 QP 有唯一编号 QPN,并绑定一个 PD 和一个 CQ。QP 的状态迁移是理解 RDMA 的关键:

  • RESET:刚创建,不可用。
  • INIT:可接收,但还不能发送;需设置 pkey、端口号、访问权限。
  • RTR(Ready to Receive):可接收对端请求;需设置目标 QPN、路径 MTU、目标 LID/GID。
  • RTS(Ready to Send):可发送和接收;需设置超时、重试次数、起始 PSN。

只有双方 QP 都进入 RTS,且参数匹配,才能建立可靠连接。QP 类型决定了通信语义:RC(可靠连接)提供可靠有序传输,适合存储与数据库;UD(不可靠数据报)支持一对多,适合管理报文;UC 介于两者之间。

三、完成队列 CQ:异步完成的收割点

RDMA 操作是异步的。应用提交 WR 后不会阻塞等待,而是通过 CQ 获知完成。CQ 是一个环形缓冲区,每个 CQE 包含:完成状态、操作码、QPN、WR ID、字节数等。应用通常用 ibv_poll_cq 轮询,或结合 ibv_get_cq_event 使用事件通知。

关键点:一个 CQ 可以服务多个 QP,但每个 QP 的 SQ 和 RQ 可以绑定不同 CQ。CQE 中的 wr_id 是应用提交 WR 时携带的标识,用于把完成事件映射回原始请求。若 CQE 状态非 0,说明操作失败,需结合 ibv_wc_status_str 排查。

四、内存注册 MR:把虚拟地址交给网卡

RDMA 网卡需要直接访问应用缓冲区,因此内存必须“注册”。注册过程会锁定物理页、建立地址翻译表,并返回两个关键值:

  • lkey:本地密钥,用于本地访问该 MR。
  • rkey:远端密钥,随连接信息传给对端,对端凭此访问本端内存。

注册时需指定访问权限:IBV_ACCESS_LOCAL_WRITE、IBV_ACCESS_REMOTE_READ、IBV_ACCESS_REMOTE_WRITE、IBV_ACCESS_REMOTE_ATOMIC。权限不足会导致远端操作被拒绝。MR 注册有开销,大页和预注册能显著降低延迟。

五、RoCEv2 与 InfiniBand 对比

两者都承载 RDMA 语义,但底层链路不同。InfiniBand 使用专用链路层和子网管理器,原生支持 RDMA,时延极低、拥塞控制成熟,但需要专用交换机和网卡。RoCEv2 把 RDMA 封装在 UDP/IP 之上,可运行在标准以太网,部署成本低,但依赖无损网络(PFC/ECN)避免丢包,配置复杂度更高。

维度InfiniBandRoCEv2
链路层专用 IB 链路以太网 + UDP/IP
寻址LID/GIDIP + UDP 4791
拥塞控制基于信用,成熟依赖 PFC/ECN,需调优
部署成本高,专用设备低,复用现有以太网
典型场景HPC、AI 训练集群云数据中心、存储网络

选择时需权衡:追求极致时延和稳定性选 InfiniBand;追求兼容性和成本选 RoCEv2,但必须做好无损网络配置。

RDMA 核心资源与数据路径 应用进程 ibv_post_send / poll_cq Verbs 接口 libibverbs 用户态 RDMA 资源 PD · MR · QP · CQ 内存注册 MR ibv_reg_mr(pd, buf, size, access) 返回 lkey / rkey 锁定物理页 · 建立地址翻译 QP 状态迁移 RESET → INIT INIT → RTR RTR → RTS 仅 RTS 可收发数据 完成队列 CQ CQE: status / opcode QPN / wr_id / byte_len ibv_poll_cq 收割 一个 CQ 可服务多个 QP RoCEv2 与 InfiniBand 协议栈对比 InfiniBand RDMA 语义 → IB 传输层 → IB 链路层 → 物理层 LID/GID 寻址 · 基于信用流控 · 专用交换机 低时延 · 高稳定 · 部署成本高 RoCEv2 RDMA 语义 → IB 传输层 → UDP/IP → 以太网 IP + UDP 4791 寻址 · 依赖 PFC/ECN 无损 兼容以太网 · 成本低 · 需网络调优 对比

六、从调用到发包:一次 RDMA WRITE 的完整路径

假设应用要执行一次 RDMA WRITE,把本地缓冲区数据写到远端。流程如下:

  1. 应用在本地注册 MR,获得 lkey;远端注册 MR,获得 rkey 并通过带外通道交换。
  2. 双方 QP 迁移到 RTS,交换 QPN、GID、PSN 等连接信息。
  3. 应用构造 ibv_send_wr,设置 opcode 为 IBV_WR_RDMA_WRITE,填入远端地址、rkey、本地 lkey。
  4. 调用 ibv_post_send 提交到 SQ,网卡异步处理。
  5. 网卡读取本地内存,封装成 RDMA 报文,经 RoCEv2 或 IB 链路发出。
  6. 远端网卡收到后,凭 rkey 校验权限,直接写入目标内存,并生成 ACK。
  7. 本端网卡收到 ACK 后,在 CQ 中写入 CQE;应用轮询到完成,确认写入成功。

整个过程对端 CPU 不参与,数据路径完全在网卡与内存之间完成。这也是 RDMA 能实现微秒级时延、极低 CPU 占用的根本原因。

实践提示:调试 RDMA 程序时,优先检查三件事——MR 权限是否包含远端访问、QP 是否已到 RTS、CQE 状态码是否为 0。多数“连不上”或“写失败”都源于这三处。
注意:RoCEv2 在丢包时性能会急剧下降,因为 RDMA 假设网络无损。生产环境必须配置 PFC 和 ECN,否则重传会抵消 RDMA 的时延优势。