2、RDMA基础回顾:RDMA概念、InfiniBand与RoCEv2协议栈、内核态与用户态数据通路对比
2.1 RDMA核心概念与价值定位
RDMA(Remote Direct Memory Access)的核心思想是:允许一端主机的应用直接读写另一端主机的内存,而整个过程不经过两端操作系统的内核,也不占用两端CPU。传统网络路径中,数据从网卡到内核协议栈、再从内核拷贝到用户态缓冲区,经历了多次拷贝和上下文切换;而RDMA通过网卡硬件(HCA/RNIC)完成传输层处理,配合内核态注册的内存区域(MR),实现“零拷贝”和“内核旁路”。
RDMA的三大核心特性可归纳为:
- 内核旁路(Kernel Bypass):应用与网卡之间直接交换描述符,不经过socket层和TCP/IP协议栈。
- 零拷贝(Zero-Copy):数据在网卡与用户缓冲区之间直接DMA搬运,无需中间缓冲。
- CPU卸载(Offload):数据的封装、解封装、可靠性确认、乱序重组等由网卡硬件完成。
2.2 InfiniBand与RoCEv2协议栈
RDMA的物理实现主要有三种网络:InfiniBand(IB)、RoCEv2(RDMA over Converged Ethernet version 2)和iWARP。其中前两者在生产环境中占绝对主导地位。理解它们的协议栈差异,是后续优化数据通路的基础。
| 层次 | InfiniBand | RoCEv2 |
|---|---|---|
| 上层协议 | IB Transport (RC/UC/UD) | IB Transport (RC/UC/UD) |
| 网络层 | IB Global Route (LRH+GRH) | UDP/IPv4或IPv6(端口4791) |
| 数据链路层 | IB Link Layer (LRH) | 以太网MAC + VLAN |
| 物理层 | IB物理层(IB线缆/光模块) | 以太网物理层(RJ45/光模块) |
| 流控机制 | 基于信用(Credit)的链路层流控 | 依赖PFC(优先级流控)保证无损 |
| 路由方式 | IB子网管理器(SM) | 传统IP路由(ECMP等) |
RoCEv2之所以采用UDP封装(目的端口4791),是为了让报文能够通过传统IP网络路由,同时利用UDP的熵源(Entropy)做负载均衡。但这也带来了一个关键问题:UDP本身不保证可靠性和顺序,因此RoCEv2网络必须依赖以太网的无损特性(PFC、ECN等)来避免丢包,否则RDMA的可靠传输(RC)会因重传而性能骤降。
2.3 内核态与用户态数据通路对比
RDMA编程模型中,控制面(创建QP、注册MR、分配CQ等)必须通过内核驱动完成,而数据面(post_send、poll_cq)则有两种实现方式:
- 用户态数据通路(Userspace Direct):应用通过
ibv_reg_mr()注册内存后,内核将网卡的硬件队列(Doorbell、WQ)mmap映射到用户空间。应用直接写Doorbell寄存器通知网卡取走WQE(Work Queue Entry),完成时网卡直接写CQE到用户态内存。全程无系统调用。 - 内核态数据通路(Kernel Direct):驱动在内核态维护WQ和CQ,应用通过
write()或sendmsg()等系统调用进入内核,由内核驱动填充WQE并ring Doorbell。完成时内核驱动处理CQE并唤醒等待的进程。
// 用户态数据通路核心流程(伪代码)
struct ibv_qp *qp;
struct ibv_mr *mr;
struct ibv_send_wr wr = { .opcode = IBV_WR_SEND };
// 1. 注册内存区域(仅一次,内核参与)
mr = ibv_reg_mr(pd, buf, size, IBV_ACCESS_LOCAL_WRITE);
// 2. 数据面:直接写WQE到硬件队列(无系统调用)
wr.sg_list = &sg; wr.num_sge = 1;
ibv_post_send(qp, &wr, &bad_wr); // 内部写Doorbell
// 3. 轮询完成队列(无系统调用)
while (ibv_poll_cq(cq, 1, &wc) == 0);
两种路径的性能差异非常显著:
| 维度 | 用户态数据通路 | 内核态数据通路 |
|---|---|---|
| 单次操作延迟 | 约1~3μs(纯硬件路径) | 约5~10μs(含系统调用+调度) |
| CPU占用 | 发送端CPU几乎为0 | 每次操作消耗约1~2μs CPU |
| 上下文切换 | 无 | 每次操作至少2次切换 |
| 适用场景 | 高频小消息、低延迟交易 | 块存储、文件系统等内核组件 |
2.4 案例:为什么存储系统选择内核态数据通路
以NVMe over RDMA为例,存储服务通常运行在内核态(如Linux的nvme-rdma驱动)。原因在于:
- 块设备语义需要内核参与:块设备请求(bio)必须经过内核的I/O调度器和块层,无法直接暴露给用户态。
- 内存注册管理:存储数据缓冲往往由内核的页缓存(Page Cache)管理,用户态无法直接注册这些页面。
- 稳定性与隔离:内核态驱动可以更好地处理错误恢复和QP重连,而用户态应用崩溃可能导致QP状态残留。
但内核态路径的代价是:每个I/O请求都要经历一次系统调用和一次数据拷贝(从用户缓冲区到内核缓冲区)。为了优化,现代存储系统采用轮询模式(Polling Mode)代替中断模式,减少上下文切换,但这会占用一个CPU核持续轮询CQ。
2.5 误区与复盘:数据通路选择的决策框架
在实际项目中,工程师常犯的错误是:在用户态应用中使用内核态RDMA接口(如通过rdma_cm的rdma_write()),导致性能远低于预期。复盘时需明确:
- 误区1:混淆“RDMA CM”与“数据通路”。RDMA CM(Connection Manager)仅负责建立连接,它本身是内核态实现,但连接建立后,数据面仍可切换到用户态直接post_send。
- 误区2:忽略内存注册的开销。用户态虽然数据面无系统调用,但
ibv_reg_mr()需要内核参与并做页表锁定,频繁注册/注销MR会严重拖慢性能。应使用内存池预注册策略。 - 误区3:认为RoCEv2与IB性能完全一致。RoCEv2在无损以太网下性能接近IB,但一旦发生PFC暂停或ECN标记,延迟会急剧恶化。IB的信用流控天然避免缓冲区溢出,而RoCEv2依赖外部网络调优。
决策框架总结如下:
| 应用类型 | 推荐数据通路 | 理由 |
|---|---|---|
| 高频交易、分布式锁 | 用户态 | 追求极致低延迟,无内核I/O依赖 |
| 分布式文件系统(Ceph) | 内核态 | 需要与内核页缓存、VFS深度集成 |
| 数据库(MySQL/PostgreSQL) | 用户态(绕过OS) | 数据库自带缓冲池,无需内核页缓存 |
| KV存储(Redis) | 用户态 | 纯内存操作,无块设备参与 |