RDMA 基本操作类型,如果对应到内核协议栈方案中,就是应用程序调用套接字 API 执行的 Send、Receive 之类的数据收发操作。RDMA 也支持 Send 和 Receive,除此之外,它还支持其他一系列操作。
本文将重点介绍 Send 和 Receive 、RDMA Write 、RDMA Read 三种基本的 RDMA 操作类型,并简要介绍其他操作类型。此外,还会介绍 MR 和 PD 机制,用来解决在实际的数据收发过程中遇到的获取物理地址、换页以及安全保障相关的问题。
一、Send 和 Receive:双端操作
1.1 核心概念
Send(发送)和 Receive(接收)是一种双端操作,因为完成一次通信过程需要两端 CPU 共同参与,并且接收端需要提前显式地下发 WQE 给硬件,否则硬件不知道如何处理接收到的数据(比如应该把数据保存到内存中的哪个地方)。
1.2 操作流程
一次 Send 和 Receive 操作的过程:
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 1(发送端) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 应用程序 │
│ │ │
│ │ 调用 ibv_post_send │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ WR(工作请求) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 驱动程序 │
│ │ │
│ │ 向 SQ 添加 WQE │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ SQ(发送队列) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ WQE 0 │ │ │
│ │ │ opcode = Send │ │ │
│ │ │ addr = 0x12340000 │ │ │
│ │ │ len = 100 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ WQE 1 │ WQE 2 │ ... │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 主机内存(MR) │
│ │ 源数据缓存 addr: 0x12340000, len = 100 │
│ │ │
│ ▼ │
│ RDMA 网卡 │
│ │ 从 SQ 获取 WQE │
│ │ 通过 DMA 读取主机内存数据 │
│ │ │
└──────┼──────────────────────────────────────────────────────────────────┘
│
│ 网络链路
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 2(接收端) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RDMA 网卡 │
│ │ 接收网络数据 │
│ │ 根据 RQ 的 WQE 将数据写入主机内存 │
│ ▼ │
│ 主机内存(MR) │
│ │ 目的数据缓存 addr: 0x11110000, len = 100 │
│ ▼ │
│ 驱动程序 │
│ │ 管理 RQ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ RQ(接收队列) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ WQE 0 │ │ │
│ │ │ addr = 0x11110000 │ │ │
│ │ │ len = 100 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ WQE 1 │ WQE 2 │ ... │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ 向 RQ 添加 WQE(提前下发) │
│ │ │
│ 应用程序 │
│ │ 调用 ibv_post_recv │
│ │ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ WR(工作请求) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
1.3 关键特点
| 特点 | 说明 |
|---|---|
| 双端操作 | 完成一次通信需要两端 CPU 共同参与 |
| 提前准备 | 接收端必须提前下发 WQE 给硬件 |
| 发送端无感知 | 发送端不知道发送的数据会被保存到接收端哪段缓存 |
| 每次准备 | 发送端每次发送数据时,接收端都要提前准备好接收数据的缓存 |
1.4 API 调用
| 操作 | API | 说明 |
|---|---|---|
| Send | ibv_post_send |
发送端调用,向 SQ 添加发送任务 |
| Receive | ibv_post_recv |
接收端调用,向 RQ 添加接收任务 |
重要说明:
发送端和接收端都需要轮询到 CQE 后,才能知道自己之前发起的操作是否完成。
二、RDMA Write:单端主动写入
2.1 核心概念
RDMA Write 操作是一种由本地软件发起的主动写入远端内存的行为。
关键特点:
| 特点 | 说明 |
|---|---|
| 单端操作 | 远端 CPU 不需要参与,也感知不到何时有数据写入以及何时写入完毕 |
| 主动写入 | 本地应用程序直接写入远端内存 |
| 权限控制 | 需要提前获取远端内存的地址和密钥 |
2.2 准备阶段
在发起 RDMA Write 操作前,需要先做好一定的准备:
准备阶段:
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. 两端应用程序提前分配好缓存 │
│ 2. 两端应用程序注册 MR(Memory Region) │
│ 3. 互相交换缓存地址和密钥等信息 │
│ ├─ 方式一:通过 Send 和 Receive 操作完成 │
│ └─ 方式二:通过套接字通信完成 │
└─────────────────────────────────────────────────────────────────────────┘
2.3 操作示意
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 1 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 应用程序 │
│ │ │
│ │ 调用 ibv_post_send │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ SQ(发送队列) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ WQE 1 │ │ │
│ │ │ opcode = RDMA_Write │ │ │
│ │ │ addr = 0x12340000 (本地源地址) │ │ │
│ │ │ remote_addr = 0x11110000 (远端目的地址) │ │ │
│ │ │ len = 100 │ │ │
│ │ │ key = R_key (远端密钥) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 主机内存(MR) │
│ │ 源数据缓存 addr: 0x12340000, len = 100 │
│ │ │
│ ▼ │
│ RDMA 网卡 │
│ │ 从 SQ 获取 WQE │
│ │ 通过 DMA 读取主机内存数据 │
│ │ │
└──────┼──────────────────────────────────────────────────────────────────┘
│
│ 网络链路
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 2 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RDMA 网卡 │
│ │ 接收网络数据 │
│ │ 将数据写入主机内存 │
│ ▼ │
│ 主机内存(MR) │
│ │ 目的数据缓存 addr: 0x11110000, len = 100 │
│ │
│ 注意:远端 CPU 不参与此过程! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.4 实际工作流程
一次 RDMA Write 操作的实际工作流程包括以下步骤:
┌─────────────────────────────────────────────────────────────────────────┐
│ 请求端 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 主机内存(MR) │
│ │ 源数据缓存 │
│ │ │
│ ▼ │
│ 软件(驱动程序) │
│ │ ① 向 QP 的 SQ 下发 WQE │
│ │ ⑧ 轮询 CQ 获取 CQE 得到任务完成信息 │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ QP │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ SQ │ │ RQ │ │ │
│ │ │ │ WQE 0 │ ... │ │ │ ... │ ... │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ CQ │ │
│ │ │ CQE 0 │ ... (⑦ 新增 CQE 0) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ▼ │
│ RDMA 网卡 │
│ │ ② 从 SQ 取 WQE 解析 │
│ │ 获得源数据缓存和目的数据缓存的虚拟地址 │
│ │ 获得数据量(长度)、远端密钥(R_Key)等信息 │
│ │ ③ 查询 MR 地址转换表,得到物理地址 │
│ │ 通过 DMA 将数据从主机内存复制到硬件缓存 │
│ │ 按协议封装数据包 │
│ │ ④ 将数据包通过物理链路发送给响应端网卡 │
│ │ ⑦ 收到 ACK 后,向 CQ 添加 CQE │
│ │ │
└──────┼──────────────────────────────────────────────────────────────────┘
│
│ ④ 网络链路(发送数据包)
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 响应端 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RDMA 网卡 │
│ │ ⑤ 接收到数据包 │
│ │ 按协议解析出应用数据和目的数据缓存的虚拟地址 │
│ │ 查询本地 MR 地址转换表,将虚拟地址转换成物理地址 │
│ │ 把应用数据写入目的数据缓存 │
│ │ ⑥ 回复 ACK 报文给请求端网卡 │
│ │ │
│ ▼ │
│ 主机内存(MR) │
│ │ 目的数据缓存 │
│ │
│ 注意:响应端 CPU 全程不参与! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.5 流程步骤详解
| 步骤 | 位置 | 操作内容 |
|---|---|---|
| ① | 请求端软件 | 向 SQ 中添加一个 WQE,下发 RDMA Write 任务给网卡 |
| ② | 请求端网卡 | 从 SQ 中取出 WQE 并解析,获得源 / 目的数据缓存的虚拟地址、数据量、远端密钥等信息 |
| ③ | 请求端网卡 | 查询 MR 地址转换表得到物理地址,通过 DMA 将数据从主机内存复制到硬件缓存,封装数据包 |
| ④ | 请求端网卡 | 将数据包通过物理链路发送给响应端网卡 |
| ⑤ | 响应端网卡 | 接收数据包,解析应用数据和目的地址,查询 MR 地址转换表,将数据写入目的数据缓存 |
| ⑥ | 响应端网卡 | 回复 ACK 报文给请求端网卡 |
| ⑦ | 请求端网卡 | 收到 ACK 后,添加一个 CQE 到 CQ 中 |
| ⑧ | 请求端软件 | 通过轮询得到 CQE,取得任务完成信息 |
2.6 核心价值
一旦远端的 CPU 把内存授权给本地应用程序使用,便不再参与数据收发的过程,从而解放了远端 CPU ,也降低了通信的时延。
RDMA 的内涵所在:
本地应用程序通过准备阶段获取了对端数据缓存的地址和密钥,相当于获得了一块远端内存的读写权限。拿到权限之后,本地应用程序就可以像访问本地主机的内存一样直接对这一远端内存区域进行读写,这也是 RDMA(远程直接存储器访问)的内涵所在。
重要说明:
本地应用程序是通过虚拟地址来读写远端内存的;实际操作过程中地址的转换由两端的 RDMA 网卡完成。
三、RDMA Read:单端主动读取
3.1 核心概念
RDMA Read 也是一种单端操作,其在工作机制和流程方面和 RDMA Write 非常相似。
与 RDMA Write 的主要差别:
| 对比项 | RDMA Write | RDMA Read |
|---|---|---|
| 操作码 | IBV_WR_RDMA_WRITE | IBV_WR_RDMA_READ |
| 数据方向 | 本地 → 远端 | 远端 → 本地 |
| 行为 | 主动写入远端内存 | 主动读取远端内存 |
3.2 操作示意
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 1 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 应用程序 │
│ │ │
│ │ 调用 ibv_post_send │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ SQ(发送队列) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ WQE 1 │ │ │
│ │ │ opcode = RDMA_Read │ │ │
│ │ │ addr = 0x12340000 (本地目的地址) │ │ │
│ │ │ remote_addr = 0x11110000 (远端源地址) │ │ │
│ │ │ len = 100 │ │ │
│ │ │ key = R_key (远端密钥) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 主机内存(MR) │
│ │ 目的数据缓存 addr: 0x12340000, len = 100 │
│ │ (数据将写入此处) │
│ │ │
│ ▼ │
│ RDMA 网卡 │
│ │ 从 SQ 获取 WQE │
│ │ 通过网络链路发送请求 │
│ │ 接收响应后将数据写入主机内存 │
│ │ │
└──────┼──────────────────────────────────────────────────────────────────┘
│
│ 网络链路(发送请求)
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 计算机 2 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RDMA 网卡 │
│ │ 接收网络请求 │
│ │ 从主机内存读取数据 │
│ │ 通过网络链路回传数据 │
│ ▼ │
│ 主机内存(MR) │
│ │ 源数据缓存 addr: 0x11110000, len = 100 │
│ │ (数据从此处读取) │
│ │
│ 注意:远端 CPU 不参与此过程! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.3 实际工作流程
一次 RDMA Read 操作的实际工作流程:
┌─────────────────────────────────────────────────────────────────────────┐
│ 请求端 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 主机内存(MR) │
│ │ 目的数据缓存 │
│ │ │
│ ▼ │
│ 软件(驱动程序) │
│ │ ① 向 QP 的 SQ 下发 WQE │
│ │ ⑧ 读取 CQE 获得任务完成信息 │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ QP │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ SQ │ │ RQ │ │ │
│ │ │ │ WQE 0 │ ... │ │ │ ... │ ... │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ CQ │ │
│ │ │ CQE 0 │ ... (⑦ 新增 CQE 0) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ▼ │
│ RDMA 网卡 │
│ │ ② 从 SQ 取 WQE 解析 │
│ │ 获得对端源数据缓存和本地目的数据缓存的虚拟地址 │
│ │ 获得数据量(长度)、远端密钥(R_Key)等信息 │
│ │ ③ 将 RDMA Read 请求数据包通过物理链路发送给响应端网卡 │
│ │ ⑥ 收到数据包,解析并提取出数据 │
│ │ 查询 MR 地址转换表,获得目的数据缓存的物理地址 │
│ │ 将数据写入目的数据缓存 │
│ │ ⑦ 向 CQ 添加 CQE │
│ │ │
└──────┼──────────────────────────────────────────────────────────────────┘
│
│ ③ 网络链路(发送请求)
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 响应端 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RDMA 网卡 │
│ │ ④ 收到数据包 │
│ │ 解析出源数据缓存的虚拟地址 │
│ │ 查询本地 MR 地址转换表,转换成物理地址 │
│ │ 从此地址读取数据,复制到硬件内部缓存 │
│ │ ⑤ 将数据封装成回复数据包,发送到物理链路 │
│ │ │
│ ▼ │
│ 主机内存(MR) │
│ │ 源数据缓存 │
│ │
│ 注意:响应端 CPU 全程不参与! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.4 流程步骤详解
| 步骤 | 位置 | 操作内容 |
|---|---|---|
| ① | 请求端软件 | 向 SQ 中添加一个 WQE,下发 RDMA Read 任务给网卡 |
| ② | 请求端网卡 | 从 SQ 中取出 WQE 并解析,获得对端源 / 本地目的数据缓存的虚拟地址、数据量、远端密钥等信息 |
| ③ | 请求端网卡 | 将 RDMA Read 请求数据包通过物理链路发送给响应端网卡 |
| ④ | 响应端网卡 | 收到数据包,解析出源数据缓存的虚拟地址,查询 MR 地址转换表,转换成物理地址,读取数据到硬件缓存 |
| ⑤ | 响应端网卡 | 将数据封装成回复数据包,发送到物理链路 |
| ⑥ | 请求端网卡 | 收到数据包,解析并提取出数据,查询 MR 地址转换表获得物理地址,将数据写入目的数据缓存 |
| ⑦ | 请求端网卡 | 添加一个 CQE 到 CQ 中 |
| ⑧ | 请求端软件 | 读取 CQE,获得任务完成信息 |
3.5 关键说明
RDMA Read 跟 RDMA Write 是相反的过程,是本地应用程序主动读取远端内存的行为。同 RDMA Write 一样,除了准备阶段,远端 CPU 不需要参与,也感知不到数据被从内存中读取的过程。
重要说明:
读取到的数据,是在对端回复的报文中携带来的。
四、其他 RDMA 操作类型
4.1 操作码定义
除了前文已经介绍的几个最常见的 RDMA 操作类型,还有其他类型的 RDMA 操作。每种 RDMA 操作类型都对应一个填写到 WQE 中的操作码。
操作码定义(来自 rdma-core/libibverbs/verbs.h):
enum ibv_wr_opcode {
IBV_WR_RDMA_WRITE, // RDMA 写
IBV_WR_RDMA_WRITE_WITH_IMM, // 带立即值的 RDMA 写
IBV_WR_SEND, // 发送
IBV_WR_SEND_WITH_IMM, // 带立即值的发送
IBV_WR_RDMA_READ, // RDMA 读
IBV_WR_ATOMIC_CMP_AND_SWP, // 原子比较和交换
IBV_WR_ATOMIC_FETCH_AND_ADD, // 原子获取和加
IBV_WR_LOCAL_INV, // 本地失效
IBV_WR_BIND_MW, // 绑定内存窗口
IBV_WR_SEND_WITH_INV, // 带失效的发送
IBV_WR_TSO, // TCP 分段卸载
IBV_WR_DRIVER1, // 驱动自定义
};
4.2 带立即值的发送
操作码: IBV_WR_SEND_WITH_IMM
特点:
| 特点 | 说明 |
|---|---|
| 立即值传输 | 发送端即时传输 32 位的数据(称为立即值) |
| 不包含在数据中 | 该值作为接收通知的一部分发送给接收端,不包含在数据缓存中 |
| CQE 携带 | 接收端应用程序通过读取 CQE 中的某个字段获取立即值 |
与普通 Send 的区别:
普通 Send:
发送端 → 数据 → 接收端
接收端必须事先下发接收缓存
带立即值的 Send:
发送端 → 数据 + 32位立即值 → 接收端
立即值不写入数据缓存,而是通过 CQE 传递给接收端应用程序
4.3 带立即值的 RDMA 写
操作码: IBV_WR_RDMA_WRITE_WITH_IMM
与普通 RDMA Write 的区别:
| 对比项 | 普通 RDMA Write | 带立即值的 RDMA Write |
|---|---|---|
| 远端通知 | 不通知远端应用程序 | 会通知远端应用程序 |
| RQ 消耗 | 不消耗远端 RQ 的 WQE | 消耗远端 RQ 的一个 WQE |
| 立即值传递 | 无 | 32 位立即值通过 CQE 传递 |
流程说明:
1. 远端应用程序先发起一次 Receive 操作
2. 远端应用程序轮询 CQE
3. 本地应用程序发起带立即值的 RDMA 写
4. 立即值不会写入远端内存
5. 立即值作为 CQE 的一部分被远端应用程序获取
4.4 原子操作
原子获取和加
操作码: IBV_WR_ATOMIC_FETCH_AND_ADD
功能:
以原子方式:
1. 将指定内存地址中的值增加指定的数值
2. 将加之前的数值返回给调用者
原子比较和交换
操作码: IBV_WR_ATOMIC_CMP_AND_SWP
功能:
以原子方式:
1. 将某个内存地址中的数值与指定数值进行比较
2. 如果它们相等,则另一个新值将被写入该内存地址
概念性代码示例:
int CAS(long *addr, long old, long new)
{
/* 原子执行 */
if(*addr != old)
return 0; // 比较失败,不写入
*addr = new;
return 1; // 比较成功,写入新值
}
CAS 的作用:
原子比较和交换(CAS)是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时写某一数据时由于执行顺序的不确定性以及中断的不可预知性产生的数据不一致问题。
应用场景:
1. 记录下某块内存中的旧值
2. 对旧值进行一系列的操作后得到新值
3. 通过 CAS 操作将新值与旧值进行交换
结果:
- 如果内存中的值未被修改过 → CAS 成功,内存值更新为新值
- 如果内存中的值已被修改过 → CAS 失败,新值不写入内存
五、RDMA 操作类型总结
5.1 操作类型对比
| 操作类型 | 操作码 | 类型 | 远端 CPU 参与 | 数据方向 | 典型场景 |
|---|---|---|---|---|---|
| Send | IBV_WR_SEND | 双端 | 需要 | 本地 → 远端 | 控制消息、小数据 |
| Receive | - | 双端 | 需要 | 本地 ← 远端 | 配合 Send 使用 |
| RDMA Write | IBV_WR_RDMA_WRITE | 单端 | 不需要 | 本地 → 远端 | 大数据传输、日志复制 |
| RDMA Read | IBV_WR_RDMA_READ | 单端 | 不需要 | 本地 ← 远端 | 远程内存访问、分布式缓存 |
| Send With Imm | IBV_WR_SEND_WITH_IMM | 双端 | 需要 | 本地 → 远端 + 32 位立即值 | 带通知的消息传递 |
| RDMA Write With Imm | IBV_WR_RDMA_WRITE_WITH_IMM | 单端 | 通知 | 本地 → 远端 + 32 位立即值 | 带通知的数据写入 |
| Atomic Fetch and Add | IBV_WR_ATOMIC_FETCH_AND_ADD | 单端 | 不需要 | 原子操作 | 分布式计数器 |
| Atomic CAS | IBV_WR_ATOMIC_CMP_AND_SWP | 单端 | 不需要 | 原子操作 | 分布式锁、无锁数据结构 |
5.2 双端操作 vs 单端操作
双端操作:
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ 发送端 接收端 │
│ │ │ │
│ │ Post Send │ Post Receive(提前准备) │
│ ▼ ▼ │
│ │ │ │
│ │ ───────── 数据 ─────────────────→ │ │
│ │ │ │
│ │ ▼ │
│ │ 数据写入缓存 │
│ │ │ │
│ │ 轮询 CQE │
│ ▼ │ │
│ 轮询 CQE │ │
│ │
│ 特点:两端 CPU 都参与数据收发过程 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
单端操作:
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ 请求端 响应端 │
│ │ │ │
│ │ Post Send(准备阶段获取地址/密钥) │ │
│ ▼ │ │
│ │ │ │
│ │ ───────── 数据 ─────────────────→ │ │
│ │ │ │
│ │ ▼ │
│ │ 数据写入缓存 │
│ │ (CPU 不感知!) │
│ │ │ │
│ ▼ │ │
│ 轮询 CQE │ │
│ │
│ 特点:响应端 CPU 全程不参与数据收发过程 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5.3 术语说明
| 术语 | 说明 | 适用场景 |
|---|---|---|
| 发送端 / 接收端 | Send/Receive 双端操作 | 描述双端操作 |
| 请求端 / 响应端 | RDMA Write/Read 单端操作 | 描述单端操作,不易产生歧义 |
六、关键设计理念
6.1 虚拟地址与物理地址转换
应用程序使用虚拟地址:
- WQE 中填写的是虚拟地址
- 应用程序通过虚拟地址来读写远端内存
实际操作中的地址转换:
- 由两端的 RDMA 网卡完成
- 网卡查询 MR 地址转换表
- 将虚拟地址转换成物理地址
- 然后进行 DMA 操作
6.2 MR 地址转换表
| 概念 | 说明 |
|---|---|
| MR(Memory Region) | 内存区域,应用程序注册的内存块 |
| 地址转换表 | 虚拟地址到物理地址的映射表 |
| 查询主体 | RDMA 网卡硬件 |
| 作用 | 支持虚拟地址访问,实现内存保护 |
6.3 权限控制机制
准备阶段:
1. 远端应用程序分配内存缓存
2. 远端应用程序注册 MR,获得 R_Key
3. 远端应用程序将虚拟地址 + R_Key 发送给请求端
4. 请求端获得远端内存的访问权限
执行阶段:
- 请求端在 WQE 中填写远端虚拟地址和 R_Key
- 响应端网卡验证 R_Key 的有效性
- 验证通过后允许访问
安全保障:
- 只有持有正确 R_Key 的请求才能访问对应内存
- MR 可以设置访问权限(只读/只写/读写)