深入浅出 RDMA 编程:高性能网络技术全解析
深入浅出 RDMA 编程:高性能网络技术全解析
在传统网络编程中,大多数应用依赖于 Socket API(如 connect、accept、send、recv)来实现端到端的数据传输。然而,当应用对极低延迟和高吞吐量有严苛要求时(如高性能计算、AI 训练、分布式存储),传统的 Socket 协议栈暴露出明显的瓶颈:频繁的上下文切换、内存拷贝以及 CPU 中断处理。
RDMA(Remote Direct Memory Access,远程直接内存访问) 技术的出现正是为了打破这一瓶颈。它允许一台计算机直接读写另一台计算机的内存,全程无需双方操作系统的介入,从而极大地降低了延迟并释放了 CPU 资源。
本文将从核心机制、通信模型、Verbs API 到完整代码示例,带你全面剖析 RDMA 编程。
一、Socket vs RDMA:数据路径对比
传统 Socket 应用的数据发送路径经过层层转发:
1 | 应用层 → Socket库 → 系统调用 → OS TCP栈 → 设备驱动 → 硬件 → 发送 |
而 RDMA 的数据路径则大幅缩短:
1 | 应用层 → RDMA库 → 硬件 → 发送(操作系统完全不参与) |

RDMA 中驱动仍然存在,但它只在控制路径(资源分配、连接建立)中使用。一旦初始化完成,数据路径便完全不经过操作系统,这种分离使 RDMA 的数据传输速度显著快于 Socket 方式。
二、RDMA 的三大核心机制
RDMA 之所以能实现突破性的性能提升,主要依赖于以下三个底层机制:
-
传输卸载(Transport Offload)
在传统的 TCP/IP 网络中,操作系统内核必须负责数据的分片、封装网络报头以及重组。而在 RDMA 中,这些繁重的协议栈处理工作被完全"卸载"到了专用的 RDMA 网卡(RNIC)硬件上。
-
内核旁路(Kernel Bypass)
当用户态的应用程序需要发送或接收数据时,数据直接在应用程序和网卡硬件之间传递,完全跳过了操作系统内核。这意味着消除了昂贵的系统调用开销和内核态/用户态的上下文切换。
-
硬件操作与原子操作(Hardware & Atomic Operations)
RDMA 提供了一系列专门的硬件指令,使得节点间的数据传输和并发状态同步更加高效。
三、通信模型:双边 vs 单边
理解 RDMA 的关键,在于区分两种截然不同的通信模型:
| 模型 | 描述 | 接收方是否参与 |
|---|---|---|
| 双边通信(Two-Sided) | 发送方发送数据,接收方主动接收并写入指定内存 | ✅ 参与 |
| 单边通信(One-Sided) | 发送方发送数据,数据直接写入目标内存地址 | ❌ 不参与 |
RDMA 即是硬件层面对单边通信模型的支持。消息中携带了目标内存地址,接收端硬件直接将数据写入对应位置,接收方软件无需介入。

四、零拷贝(Zero Copy)技术
在单边通信模型中,有两种数据发送策略:

| 方式 | 额外拷贝 | 适用场景 |
|---|---|---|
| Buffer Copy | 有(2 次拷贝) | 小消息 |
| Zero Copy | 无 | 大消息 ✅ |
利用 RDMA 的单边通信,发送方直接将数据灌入接收方的最终目标内存,不仅消除了内存间的额外拷贝,还极大降低了 CPU 负载,是超大数据块传输的理想选择。
五、传输服务类型:RC 与 UD
类似于传统网络中的 TCP 和 UDP,RDMA 主要提供两种传输服务:
| 传输类型 | 全称 | 可靠性 | 连接方式 | 类比协议 |
|---|---|---|---|---|
| RC | Reliable Connection | ✅ 可靠 | 面向连接 | TCP |
| UD | Unreliable Datagram | ❌ 不可靠 | 无连接 | UDP |
- RC 保证消息必达,通过序列号检测丢失消息并触发重传,适合大多数客户端-服务器场景。
- UD 每条消息可发往不同目标,提供更低延迟和更高可扩展性,通常用于多播或对规模要求极高的特定场景。
六、核心 API:深入理解 Verbs 与三大对象
开发 RDMA 应用通常使用 libibverbs 用户态库。Verbs API 被划分为:
- 控制路径(Control Path):资源分配、修改与释放,涉及操作系统
- 数据路径(Data Path):发送和接收数据,必须快速高效,不涉及 OS
核心对象三要素

| 对象 | 说明 |
|---|---|
| Queue Pair(QP,队列对) | 包含发送队列(SQ)和接收队列(RQ),是通信的基本端点,类似 Socket |
| Completion Queue(CQ,完成队列) | 硬件将操作结果写入 CQ,应用程序通过轮询 CQ 得知任务是否完成 |
| Memory Region(MR,内存区域) | 向网卡注册的内存,供 RDMA 操作直接访问 |
七、内存注册机制(Memory Registration)
由于网卡硬件需要绕过 OS 直接访问内存,应用程序必须提前将用于通信的内存区域"注册"到网卡。注册内存有三个核心属性:
- 保护与权限(Protection):定义虚拟地址范围的读/写权限,区分本地访问和远程访问
- 内存锁定(Memory Pinning):将物理内存锁定,防止被换出到磁盘,确保 RDMA 操作时页面始终在 RAM 中
- 转换句柄(Translation Handle):生成
lkey(本地访问键)和rkey(远程访问键),供硬件寻址使用

⚠️ 注意顺序:必须先注册再使用,必须先注销再释放,顺序不能颠倒。
代码示例:
1 | // 注册内存区域 |
八、四种核心 RDMA 操作(OpCodes)
在组装工作请求(Work Request,WR)时,需要指定操作类型:

1. Send / Receive(双边)
接收方必须提前投递 Receive WR 提供空闲缓冲区;随后发送方投递 Send WR,数据到达后消耗接收方的一个缓冲区,可靠传输模式下接收方回送 ACK。
2. RDMA Write(单边)
发送方在消息中包含远端目标地址,数据到达后由接收端硬件自动写入,接收方软件完全不参与,完成后硬件向发送方返回 ACK。
3. RDMA Read(单边)
发送方主动去远程节点的指定内存地址中拉取数据到本地,接收端硬件读取数据并返回,接收方软件无需参与。
4. Atomic(原子操作)
依赖硬件能力,保证多步骤操作不可中断。支持:
- Fetch and Add:读取当前值 → 加上指定数值
- Compare and Swap:比较当前值 → 若匹配则替换
类比理解:原子操作就像你在厨房同时尝汤和加盐——不会出现你加完盐后室友又加一次盐的问题,保证"读-改-写"不被打断。
九、数据面核心代码示例
发送消息
1 | // 构建 Scatter-Gather Entry |
接收消息
1 | struct ibv_recv_wr recv_wr = { |
轮询完成队列
1 | struct ibv_wc wc[MAX_WC]; |

注意:Work Request 在提交后处于 Outstanding(待完成) 状态,直到 CQ 中出现对应的 Work Completion 才能再次访问对应缓冲区。
十、完整的 RDMA 编程生命周期(以 RC 模式为例)
一个完整的 RDMA 程序(如经典的 Pingpong 测试)通常遵循以下生命周期:
1. 资源初始化与内存注册
- 打开设备(
ibv_open_device)并分配保护域(ibv_alloc_pd) - 申请应用内存,使用
ibv_reg_mr注册,获取lkey和rkey - 创建完成队列 CQ(
ibv_create_cq) - 创建队列对 QP(
ibv_create_qp)并将其与 CQ 绑定
2. 解决"鸡与蛋"的连接问题
在 RC 模式下,QP 之间需要建立连接,这要求双方知道对方的 QP Number(类似端口)、IP/MAC 地址以及用于单边通信的 rkey。
- 带外通信(Out-of-band):通常先建立一个普通的 TCP Socket 连接,用它来交换这些 RDMA 元数据。
- RDMA CM:也可以使用专用的 RDMA Connection Manager 库,它提供了类似 TCP 的
rdma_listen、rdma_connect接口来自动完成握手。
3. QP 状态机转换
建立连接的过程中,QP 的状态必须手动进行严格的跃迁:
| 状态 | 全称 | 说明 |
|---|---|---|
| INIT | 初始化 | 配置基本属性 |
| RTR | Ready To Receive | 配置目标 QP Number 和包序号(PSN),此时可以接收数据 |
| RTS | Ready To Send | 配置超时和重传参数,此时可以发送数据 |
4. 数据面收发循环(Data Path)
- 构建请求:准备 Scatter/Gather Element(SGE),指定内存地址、长度和
lkey - 投递请求:调用
ibv_post_recv预埋接收缓冲区,调用ibv_post_send发送数据 - 轮询完成状态:在循环中调用
ibv_poll_cq检查操作是否完成,解析状态码(如IBV_WC_SUCCESS表示成功,IBV_WC_RETRY_EXC_ERR表示重传超时)
5. 资源清理
通信结束后,按照与创建相反的顺序销毁资源:
1 | 断开 QP → 销毁 CQ → 注销内存(ibv_dereg_mr)→ 释放保护域 → 关闭设备 |
十一、适用场景
RDMA 技术广泛应用于对延迟和吞吐量极为敏感的领域:
| 领域 | 典型用途 |
|---|---|
| HPC(高性能计算) | MPI 集合通信、并行计算 |
| AI/ML 训练 | 大规模分布式训练中的梯度同步(如 NCCL) |
| 云计算与存储 | NVMe-oF、分布式存储(如 Ceph) |
| 金融领域 | 低延迟高频交易系统 |
RDMA 的底层网络可以是专用的 InfiniBand,或在标准以太网上运行的 RoCE(RDMA over Converged Ethernet),前者是专用高性能网络,后者允许在现有网络基础设施上部署 RDMA。
总结
RDMA 编程打破了操作系统协议栈的桎梏,将网络通信的控制权和数据通路直接交给了硬件和用户态程序。其核心价值可以概括为:
- 传输卸载:协议栈处理由 RNIC 硬件承担,释放 CPU
- 内核旁路:数据路径完全不经过 OS,消除系统调用开销
- 单边通信:接收方 CPU 零介入,实现真正的零拷贝
虽然 RDMA 初期学习曲线较陡,涉及大量底层的内存和队列管理,但当你熟练掌握 Verbs API 并合理利用单边通信(RDMA Write/Read)时,你将能够构建出具有极致吞吐量和微秒级延迟的顶级高性能应用。



