第一部分 · 2 / 6

Verbs 对象模型

RDMA 的编程接口叫 Verbs,用户态实现是 libibverbs。 它不像 socket 那样只有一个文件描述符,而是一组协作的对象。gordma 的底层包正是逐一封装了它们, 所以先把这些名词认全,后面读代码就顺了。

对象全景

对象全称作用
Device设备一块 RDMA 网卡(HCA / Channel Adapter),如 mlx5_0
Context设备上下文打开设备后的句柄,后续所有资源都从它派生。
PDProtection Domain保护域,把 MR / QP 等资源归到同一组,做隔离与权限边界。
MRMemory Region注册(pin 住)的内存区域,网卡只能访问已注册内存。返回 lkey/rkey
CQCompletion Queue完成队列。每个操作完成后,网卡往这里放一个完成事件(WC)。
QPQueue Pair队列对 = 发送队列(SQ) + 接收队列(RQ)。RDMA 的「连接」端点,类比 socket。
AHAddress Handle地址句柄,UD 传输里描述对端地址。
CompChannelCompletion Channel完成通道,让你能「等待」CQ 上的事件而不必忙等轮询。

它们之间的派生关系大致是:Device → Context → {PD, CQ},再由 PD → {MR, QP}

Device
  └─ Context (ibv_open_device)
       ├─ PD (ibv_alloc_pd)
       │    ├─ MR  (ibv_reg_mr)      ← 注册内存,得 lkey/rkey
       │    └─ QP  (ibv_create_qp)   ← 收发端点
       ├─ CQ (ibv_create_cq)         ← 完成队列
       └─ CompChannel (可选)         ← 事件驱动等待 CQ

内存为什么必须注册

这是 RDMA 编程绕不开、也最反直觉的一步。网卡要 DMA 访问主存,必须满足两个前提:

  • 内存被 pin 住:操作系统不能把这页换出或迁移,否则网卡拿到的物理地址会失效。
  • 网卡知道这段内存:把虚拟地址→物理地址的映射、访问权限登记进网卡。

注册(ibv_reg_mr)一次完成上述两件事,返回两把钥匙:

钥匙用途
lkey(local key)本地工作请求引用自己的缓冲区时用。
rkey(remote key)授权远端对这段内存做 RDMA Read/Write,需带外发给对方。
代价 注册内存有成本(pin 页 + 建映射),所以高性能程序通常预先注册一块大缓冲区反复用, 而不是每次收发都注册。gordma 的高层 rdmanet 就替你维护了一池注册好的「bounce buffer」。

工作请求与完成队列

RDMA 是异步的。你不「调用一个发送函数等它返回」,而是:

  1. 把一个工作请求(Work Request, WR)投递(post)到 QP 的发送/接收队列。
  2. 网卡在后台异步执行它。
  3. 完成后,网卡往完成队列 CQ 里放一个完成项(Work Completion, WC)
  4. 轮询 CQ(或被 CompChannel 事件唤醒)拿到 WC,确认成功与否。

WC 里带着你 post 时填的 WRID、状态、传输字节数等,所以能把完成项和原始请求对上号。

下一步 对象里最核心、状态最复杂的是 QP。下一节专门讲 QP 与它的状态机—— 这是初学者最容易卡住的地方。
gordma 教程