RDMA CM UDP 通信完整指南

RDMA CM UDP 通信完整指南

  1. 📋 项目概述

这是一个使用 RDMA CM (Connection Manager) 和 UDP 协议进行通信的完整示例,包含服务端和客户端实现。

核心特性

  • ✅ 使用 RDMA CM 简化地址解析和路由查找
  • ✅ 基于 UD (Unreliable Datagram) QP 类型
  • ✅ 支持 InfiniBand 和 RoCE 自动切换
  • ✅ 事件驱动的完成处理 (服务端)
  • ✅ 轮询模式的完成处理 (客户端)
    与普通 UDP 的区别
    特性普通 UDPRDMA CM UDP传输方式内核协议栈硬件卸载延迟微秒级亚微秒级CPU 使用率较高极低零拷贝否是内核旁路否是

🔧 编译

Makefile

CC = gcc

CFLAGS = -Wall -O2 -g

LDFLAGS = -lrdmacm -libverbs

all: rdma_udp_server rdma_udp_client

rdma_udp_server: server.c

(CC) (CFLAGS) -o rdma_udp_server server.c $(LDFLAGS)

rdma_udp_client: client.c

(CC) (CFLAGS) -o rdma_udp_client client.c $(LDFLAGS)

clean:

rm -f rdma_udp_server rdma_udp_client

.PHONY: all clean

手动编译

编译服务端

gcc -o rdma_udp_server server.c -lrdmacm -libverbs -Wall -O2

编译客户端

gcc -o rdma_udp_client client.c -lrdmacm -libverbs -Wall -O2

依赖检查

检查库是否安装

pkg-config --libs rdmacm ibverbs

如果未安装

sudo apt-get install libibverbs-dev librdmacm-dev # Ubuntu/Debian

sudo yum install rdma-core-devel # CentOS/RHEL


🚀 运行

  1. 启动服务端
    ./rdma_udp_server

预期输出:

=== RDMA CM UDP Server ===

Server bound to port 12345

Resources created successfully

QP Number: 0x1f

Using GRH (RoCE mode) for reply

Server ready, waiting for UDP messages...

Press Ctrl+C to stop

  1. 启动客户端

基本用法

./rdma_udp_client 192.168.1.100

发送自定义消息

./rdma_udp_client 192.168.1.100 "Hello RDMA World"

预期输出:

=== RDMA CM UDP Client ===

Resolving address 192.168.1.100:12345...

Address resolved

Route resolved

Resources created successfully

Local QP Number: 0x20

Using GRH addressing (RoCE mode)

Address Handle created

Remote QPN set to: 0x20

Sending message: Hello RDMA World

Message sent: Hello RDMA World

Waiting for reply (timeout: 5000ms)...

=== Received reply ===

From QPN: 0x1f
Message: Server reply: Echo 'Hello RDMA World'

Communication completed successfully!

  1. 服务端输出 (收到消息后)
    Waiting for completion events...

=== Received UDP message ===

From QPN: 0x20
From LID: 0x0
Message: Hello RDMA World

Using GRH (RoCE mode) for reply

Reply sent: Server reply: Echo 'Hello RDMA World'


🔍 代码核心要点

  1. RDMA_PS_UDP 协议类型
    // 创建 UDP 类型的 RDMA CM ID
    rdma_create_id(ec, &id, NULL, RDMA_PS_UDP);

与 RDMA_PS_TCP 的区别:

  • RDMA_PS_TCP: 用于 RC (Reliable Connected) QP
  • RDMA_PS_UDP: 用于 UD (Unreliable Datagram) QP
  1. 服务端:事件驱动模型
    // 创建完成通道
    comp_chan = ibv_create_comp_channel(verbs);

// 创建 CQ 并关联到通道

cq = ibv_create_cq(verbs, 16, NULL, comp_chan, 0);

// 请求事件通知

ibv_req_notify_cq(cq, 0);

// 等待事件

ibv_get_cq_event(comp_chan, &ev_cq, &ev_ctx);

// 确认事件

ibv_ack_cq_events(ev_cq, 1);

优势: CPU 效率高,不需要忙等

  1. UD 特有:GRH 空间处理

#define GRH_SIZE 40 // Global Routing Header

// 分配缓冲区时预留 GRH 空间

buffer = malloc(BUFFER_SIZE + GRH_SIZE);

// 发送时跳过 GRH

sge.addr = (uintptr_t)(buffer + GRH_SIZE);

// 接收时数据从 GRH 后开始

printf("Message: %s\n", buffer + GRH_SIZE);

  1. 从接收完成中提取远端地址
    // 提取源地址信息
    ah_attr.dlid = wc.slid; // 源 LID
    uint32_t src_qpn = wc.src_qp; // 源 QPN

// 检查是否有 GRH (RoCE)

if (wc.wc_flags & IBV_WC_GRH) {

struct ibv_grh *grh = (struct ibv_grh *)buffer;

ah_attr.grh.dgid = grh->sgid; // 源 GID

}

  1. UD 发送必须指定 AH 和远端 QPN
    wr.wr.ud.ah = ah; // Address Handle
    wr.wr.ud.remote_qpn = remote_qpn; // 远端 QP 号
    wr.wr.ud.remote_qkey = 0x11111111; // Q_Key

⚠️ 重要注意事项

  1. QPN 交换问题
    当前实现的简化处理:
    // 客户端代码中的简化 (需要改进)
    ctx.remote_qpn = ctx.id->qp->qp_num; // 使用自己的 QPN (错误)

生产环境的正确做法:

方案A: 使用额外的 TCP 连接交换

// 服务端:通过 TCP 发送 QPN

int tcp_sock = accept(...);

uint32_t my_qpn = qp->qp_num;

send(tcp_sock, &my_qpn, sizeof(my_qpn), 0);

// 客户端:通过 TCP 接收 QPN

int tcp_sock = connect(...);

uint32_t server_qpn;

recv(tcp_sock, &server_qpn, sizeof(server_qpn), 0);

ctx.remote_qpn = server_qpn;

方案B: 使用固定 QPN

// 服务端和客户端约定固定的 QPN

#define SERVER_QPN 0x100

#define CLIENT_QPN 0x200

方案C: 使用多播组 (UD 特性)

// 加入多播组

rdma_join_multicast(id, multicast_addr, NULL);

  1. Q_Key 匹配
    // 发送端和接收端必须使用相同的 Q_Key
    #define QKEY 0x11111111

// QP 初始化时设置

modify_qp_to_init(..., qkey=QKEY);

// 发送时指定

wr.wr.ud.remote_qkey = QKEY;

  1. 无连接语义
    UD 模式的特点:
  • ❌ 不保证数据到达
  • ❌ 不保证顺序
  • ❌ 无流量控制
  • ✅ 低延迟
  • ✅ 支持多播
    适用场景:
  • 实时流媒体
  • 传感器数据采集
  • 金融行情推送
  • 日志收集
  1. RoCE vs InfiniBand 自动适配
    代码已自动处理:
    if (path->dlid == 0) {
    // RoCE: 使用 GID
    ah_attr.is_global = 1;
    } else {
    // InfiniBand: 使用 LID
    ah_attr.is_global = 0;
    }

🐛 常见问题排查

问题1: "Failed to resolve address"

原因: 网络不通或 RDMA 设备未激活

排查:

检查设备

ibv_devices

检查端口状态

ibv_devinfo

测试网络

ping <server_ip>

检查 RDMA 端口

rdma link show

问题2: "Failed to create QP"

原因: 资源不足或参数错误

排查:

查看系统资源限制

ulimit -l

增加内存锁定限制

sudo sh -c "echo '* soft memlock unlimited' >> /etc/security/limits.conf"

sudo sh -c "echo '* hard memlock unlimited' >> /etc/security/limits.conf"

问题3: 接收超时

可能原因:

1.QPN 不匹配

2.Q_Key 不匹配

3.防火墙阻止

4.接收队列未投递

调试:

查看 QP 状态

rdma resource show qp

查看统计信息

rdma statistic show

抓包分析

tcpdump -i <rdma_device> -w capture.pcap

问题4: "Completion error: status=5"

错误码含义:

IBV_WC_LOC_LEN_ERR = 1 // 本地长度错误

IBV_WC_LOC_QP_OP_ERR = 2 // QP 操作错误

IBV_WC_LOC_PROT_ERR = 4 // 保护错误

IBV_WC_RNR_RETRY_EXC = 5 // RNR 重试超限

IBV_WC_WR_FLUSH_ERR = 5 // WR 刷新错误


📊 性能测试

测试延迟

修改客户端代码,添加时间戳

struct timespec start, end;

clock_gettime(CLOCK_MONOTONIC, &start);

send_message(ctx, "ping");

receive_reply(ctx, 5000);

clock_gettime(CLOCK_MONOTONIC, &end);

long latency_ns = (end.tv_sec - start.tv_sec) * 1000000000L +

(end.tv_nsec - start.tv_nsec);

printf("Round-trip latency: %ld ns\n", latency_ns);

测试吞吐量

批量发送

for (int i = 0; i < 10000; i++) {

send_message(ctx, "data");

}

对比标准 UDP

RDMA UDP

./rdma_udp_client 192.168.1.100

标准 UDP (使用 netcat)

echo "test" | nc -u 192.168.1.100 12345

预期性能差异:

  • 延迟:RDMA < 5μs,标准 UDP > 50μs
  • CPU 使用率:RDMA < 10%,标准 UDP > 80%

🚀 进阶改进

  1. 多客户端支持
    服务端需要维护客户端地址表:
    struct client_info {
    uint32_t qpn;
    uint16_t lid;
    union ibv_gid gid;
    struct ibv_ah *ah;
    };

struct client_info clients[MAX_CLIENTS];

  1. 可靠性增强

    添加应用层 ACK:

    struct udp_packet {

    uint32_t seq_num;

    uint32_t ack_num;

    uint8_t flags; // SYN, ACK, FIN

    char data[1024];

    };

  2. 多播支持

    // 加入多播组

    struct sockaddr_in mcast_addr;

    mcast_addr.sin_addr.s_addr = inet_addr("224.0.0.1");

    rdma_join_multicast(id, (struct sockaddr *)&mcast_addr, NULL);

  3. 零拷贝优化

    // 使用 inline 发送小消息

    if (msg_len <= 64) {

    wr.send_flags |= IBV_SEND_INLINE;

    }


📚 参考资料

官方文档

  • rdma_create_id(3)
  • rdma_resolve_addr(3)
  • ibv_post_send(3)
    相关示例
  • rdma-core: libibverbs/examples/ud_pingpong.c
  • Linux Kernel: drivers/infiniband/core/cma.c
    进阶阅读
  • "RDMA Aware Programming User Manual"
  • "InfiniBand Architecture Specification" - Chapter 10

✅ 检查清单

在部署前确认:

  • RDMA 设备已激活
  • 端口状态为 ACTIVE
  • 网络连通性正常
  • QPN 交换机制已实现
  • Q_Key 已正确配置
  • 防火墙规则已配置
  • 错误处理已完善
  • 性能测试已通过

总结: 这是一个完整的 RDMA CM UDP 通信示例,展示了如何使用 RDMA CM 简化 UD 通信。在生产环境中,需要特别注意 QPN 交换机制和错误处理。

祝编程愉快!🎉

相关推荐
思茂信息3 小时前
CST电动车EMC仿真——电机控制器MCU滤波仿真
javascript·单片机·嵌入式硬件·cst·电磁仿真
福尔摩斯张3 小时前
基于C++的UDP网络通信系统设计与实现
linux·c语言·开发语言·网络·c++·tcp/ip·udp
小曹要微笑3 小时前
I2C总线技术解析(纯文字版)
单片机·嵌入式硬件·esp32·iic
袖手蹲3 小时前
Arduino UNO Q 从 Arduino Cloud 远程控制闪烁 LED
人工智能·单片机·嵌入式硬件·电脑
北京耐用通信3 小时前
终结混合网络调试噩梦:耐达讯自动化实现EtherCAT对DeviceNet设备的直接读写
网络·人工智能·物联网·网络协议·自动化·信息与通信
平凡灵感码头4 小时前
第一次做蓝牙产品,从零开发 嵌入式开发日志(2)AC63NSDK 完整合并版目录说明
stm32·单片机·嵌入式硬件
千天夜5 小时前
文件系统磁盘块分配方式:从隐式链接到索引结构
网络·网络协议
2501_915918415 小时前
HTTPS 端口深度解析,443 并不是唯一入口,理解 TLS 流量行为与抓包策略
网络协议·http·ios·小程序·https·uni-app·iphone
SystickInt5 小时前
常见问题整理总结
单片机·嵌入式硬件