以下是基于 RDMA-Tutorial 开源项目编写的可运行 RDMA 通信 Demo,包含完整的代码、编译和运行步骤,并结合实际场景进行说明。
一、环境准备
- 硬件/模拟环境
- 物理 RDMA 设备:若使用 Mellanox 网卡(如 ConnectX 系列),需安装 MLNX_OFED 驱动。
- 模拟环境:若无物理设备,可通过 Soft-RoCE 模拟(Ubuntu 示例):
sudo apt install libibverbs-dev librdmacm-dev # 安装依赖 sudo modprobe rdma_rxe # 加载内核模块 sudo rxe_cfg add ens33 # 绑定网卡(替换为实际网卡名)
- 克隆项目并编译
git clone https://github.com/jcxue/RDMA-Tutorial.git cd RDMA-Tutorial mkdir build && cd build cmake .. && make # 编译项目(需已安装 CMake)
二、示例代码:RDMA 双向通信
以下是一个简化的 客户端-服务器 双向通信 Demo,基于 librdmacm
和 libibverbs
实现零拷贝数据传输。
代码文件:rdma_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <rdma/rdma_cma.h>
#define BUFFER_SIZE 1024
struct context {
struct rdma_cm_id *id;
struct ibv_mr *mr;
char *buffer;
};
// 连接建立回调
static void on_connect(struct rdma_cm_id *id) {
struct context *ctx = (struct context *)id->context;
printf("Connection established.\n");
}
// 数据接收回调
static void on_completion(struct ibv_wc *wc) {
if (wc->status == IBV_WC_SUCCESS) {
printf("Data received: %s\n", (char *)wc->wr_id);
}
}
// 服务端初始化
void run_server(const char *port) {
struct rdma_cm_event *event;
struct rdma_cm_id *listen_id;
struct rdma_addrinfo hints = { .ai_flags = RAI_PASSIVE };
// 创建监听端
rdma_create_addr_info(&listen_id, NULL, port, &hints);
rdma_listen(listen_id, 5);
while (1) {
rdma_get_cm_event(listen_id->channel, &event);
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
struct context *ctx = malloc(sizeof(struct context));
ctx->id = event->id;
ctx->buffer = malloc(BUFFER_SIZE);
ctx->mr = rdma_reg_msgs(ctx->id, ctx->buffer, BUFFER_SIZE);
rdma_accept(ctx->id, NULL); // 接受连接
}
rdma_ack_cm_event(event);
}
}
// 客户端初始化
void run_client(const char *server_ip, const char *port) {
struct rdma_cm_id *conn_id;
struct rdma_addrinfo hints = { .ai_port_space = RDMA_PS_TCP };
struct ibv_wc wc;
// 建立连接
rdma_create_addr_info(&conn_id, server_ip, port, &hints);
rdma_connect(conn_id, NULL);
// 发送数据
char *msg = "Hello from client!";
rdma_post_send(conn_id, NULL, msg, strlen(msg)+1, conn_id->mr, 0);
rdma_get_send_comp(conn_id, &wc); // 等待发送完成
}
int main(int argc, char **argv) {
if (argc == 2) {
run_server(argv[1]); // 服务端:./demo <port>
} else if (argc == 3) {
run_client(argv[1], argv[2]); // 客户端:./demo <server_ip> <port>
}
return 0;
}
三、编译与运行
- 编译命令
gcc rdma_demo.c -o rdma_demo -lrdmacm -libverbs
- 运行示例
- 服务端(监听端口 12345):
./rdma_demo 12345
- 客户端(连接至服务端):
./rdma_demo 192.168.1.100 12345
- 服务端(监听端口 12345):
- 预期输出
- 服务端:
Connection established. Data received: Hello from client!
- 客户端:
Send completed.
- 服务端:
四、关键机制解析
- 零拷贝传输
- 通过
rdma_reg_msgs()
注册内存缓冲区,RDMA 设备直接访问用户空间内存,避免内核拷贝。
- 通过
- 异步事件驱动
- 使用
rdma_get_cm_event()
监听连接事件,on_completion()
处理数据传输完成通知。
- 使用
- 连接管理
librdmacm
抽象了 RDMA 连接建立过程,支持 TCP-like 的通信模式。
五、常见问题解决
- 编译错误:确保安装
libibverbs-dev
和librdmacm-dev
。 - 连接失败:检查防火墙设置,确认服务端 IP 和端口正确。
- 设备不可用:通过
ibv_devices
验证 RDMA 设备状态,或使用 Soft-RoCE 模拟。
六、扩展应用场景
- 高性能计算:用于 MPI 进程间通信加速,减少延迟。
- 分布式存储:结合 SPDK 实现高速存储访问。
- 实时数据处理:在 Kafka/Pulsar 中优化消息队列吞吐量。
参考文档:
- RDMA-Tutorial 项目主页
- Mellanox RDMA 编程手册
- Soft-RoCE 配置指南