3、RDMA核心概念:内存注册(MR)、队列对(QP)、完成队列(CQ)、工作请求(WR)
好,咱们今天聊点实在的。RDMA 这玩意儿,说白了就是让网卡直接读写你的应用内存,不用经过 CPU 和内核。但要做到这一点,硬件得知道「你的内存长什么样」、「数据该往哪送」、「送完了怎么通知你」。这就引出了四个核心概念:MR、QP、CQ、WR。
我刚开始接触 RDMA 时,觉得这些名词又长又绕。后来发现,你只要搞懂它们各自管什么事,整个 RDMA 的骨架就搭起来了。咱们一个一个拆。
3.1 内存注册(MR)—— 给网卡开个「通行证」
RDMA 网卡要直接读写你的应用内存,但操作系统可不会随便让硬件碰内存。你得先告诉内核:「这块内存,我授权给网卡用」。这个过程就叫内存注册(Memory Registration,MR)。
注册之后,网卡会拿到两个关键东西:
- L_Key(本地键):本地操作时用的,比如把数据从这块内存发出去。
- R_Key(远程键):远程机器要读/写这块内存时用的,相当于一个密码。
我个人习惯,在分配 MR 时尽量用大块连续内存。为什么呢?因为每个 MR 注册都会消耗网卡的硬件资源,MR 数量太多,性能反而会下降。
核心要点:MR 就是给网卡开了一张「内存通行证」。没有它,RDMA 根本动不了你的数据。
小技巧:我建议你在初始化阶段就把所有需要的内存一次性注册好,不要在热路径上频繁注册/注销 MR。否则延迟会飙升,你想想看,每次发数据前都要跟内核打个招呼,那还怎么快?
3.2 队列对(QP)—— RDMA 的「通信管道」
队列对(Queue Pair,QP)是 RDMA 通信的核心。它由两个队列组成:
- 发送队列(SQ):放你要发出去的工作请求。
- 接收队列(RQ):放你准备接收的工作请求。
为什么叫「对」?因为通信双方各有一个 QP,它们之间建立连接后,数据就在这两个 QP 之间流动。你可以把 QP 想象成一根水管,一头接你的应用,另一头接远程的应用。
QP 有三种类型:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| RC(可靠连接) | 一对一,可靠,保序 | 大多数场景,比如存储、数据库 |
| UC(不可靠连接) | 一对一,不可靠,不保序 | 对丢包不敏感的场景 |
| UD(不可靠数据报) | 多对多,不可靠,最大 MTU | 多播、广播、控制面通信 |
我在项目中遇到过一个问题:QP 数量太多,导致网卡内存耗尽。当时我们一个节点要跟几百个对端通信,每个对端建一个 RC QP,结果网卡直接罢工了。后来改成 UD + 应用层重传,才把问题解决。嗯,这里要注意:QP 不是越多越好,要根据场景选类型。
避坑指南:我曾经见过有人把 QP 的发送队列深度设得特别大,觉得这样能提高吞吐。结果呢?内存占用暴涨,延迟反而变高了。QP 的队列深度要跟你的业务模型匹配,不是越大越好。
3.3 完成队列(CQ)—— 操作完成的「消息中心」
你发了一个工作请求,怎么知道它执行完了?轮询?太浪费 CPU。中断?延迟又高。RDMA 用了一个折中方案:完成队列(Completion Queue,CQ)。
CQ 里存放的是完成事件(Completion Event)。当你的发送或接收操作完成后,网卡会把结果写进 CQ。你的应用只需要从 CQ 里取事件,就知道操作成功了还是失败了。
这里有个关键点:一个 CQ 可以服务于多个 QP。我习惯把同一类业务的 QP 挂到同一个 CQ 上,这样管理起来方便,轮询线程也少。
核心要点:CQ 是异步通知机制的核心。它让你不用死等每个操作完成,而是批量处理完成事件,效率高得多。
3.4 工作请求(WR)—— 告诉网卡「干什么活」
工作请求(Work Request,WR)就是你发给 QP 的指令。它告诉网卡:
- 从哪块内存读数据(源地址)
- 写到哪块内存(目标地址)
- 数据有多长
- 操作类型(发送、接收、读、写、原子操作)
WR 的结构大致如下:
struct ibv_send_wr {
uint64_t wr_id; // 用户自定义 ID,用于匹配完成事件
struct ibv_sge *sg_list; // 散列表,描述数据在内存中的位置
int num_sge; // 散列表项数
enum ibv_wr_opcode opcode; // 操作类型
// ... 其他字段
};
你想想看,WR 就是你把任务清单交给网卡,网卡照着清单干活。干完了,往 CQ 里扔一个完成事件,告诉你「活干完了」。
个人经验:我建议你在 WR 的 wr_id 里编码一些业务信息,比如请求序号、连接 ID。这样从 CQ 取完成事件时,不用再查上下文,直接就能知道是哪个请求完成了。这个小技巧能省不少 CPU 开销。
3.5 它们怎么配合?一张图看懂
说了这么多,咱们用一张图把 MR、QP、CQ、WR 的关系串起来:
流程很简单:
- 你先注册一块内存(MR),拿到钥匙。
- 构造一个工作请求(WR),告诉网卡要干什么活。
- 把 WR 扔进队列对(QP)的发送队列或接收队列。
- 网卡从 QP 里取 WR,直接读写你的 MR 内存。
- 干完了,往完成队列(CQ)里写一个完成事件。
- 你的应用从 CQ 里取事件,知道活干完了。
说白了,MR 是「地盘」,QP 是「管道」,WR 是「任务单」,CQ 是「公告栏」。四个东西配合起来,RDMA 才能做到零拷贝、低延迟、少 CPU 干预。
总结一下:搞懂这四个概念,RDMA 的大门就打开了。后面咱们聊的每一个协议、每一种操作,都离不开它们。你先把这张图刻在脑子里,后面学起来会轻松很多。
公众号:蓝海资料掘金营,微信 deep3321