knowledge base:
librdmacm 这是一个用于 RDMA(远程直接内存访问)通信管理的用户态库。
一、librdmacm 整体架构
librdmacm(RDMA Connection Manager)是构建在 libibverbs 之上的高层库,主要提供连接管理和地址解析功能。实际开发中,通常使用 librdmacm 处理连接,然后使用 libibverbs 进行高性能数据传输。
1.1 架构层次
┌─────────────────────────────────────────┐
│ 用户应用程序 (User App) │
├─────────────────────────────────────────┤
│ librdmacm (连接管理/地址解析) │ ← 您使用的这层
│ - 连接建立/断开、地址解析、事件通知 │
├─────────────────────────────────────────┤
│ libibverbs (底层 Verbs 接口) │
│ - QP/CQ/MR 管理、实际数据传输 │
├─────────────────────────────────────────┤
│ 内核 RDMA 子系统 │
│ - uverbs、rdma_cm、IB 核心驱动 │
├─────────────────────────────────────────┤
│ RDMA 硬件 (NIC) │
│ - Mellanox、Intel、Broadcom 等 │
└─────────────────────────────────────────┘1.2 核心组件

二、核心工作流程
2.1 服务端工作流程
// 1. 创建事件通道
struct rdma_event_channel *ec = rdma_create_event_channel();
// 2. 创建 CM ID
struct rdma_cm_id *listen_id;
rdma_create_id(ec, &listen_id, NULL, RDMA_PS_TCP);
// 3. 绑定地址
struct sockaddr_in addr = {...};
rdma_bind_addr(listen_id, (struct sockaddr *)&addr);
// 4. 开始监听
rdma_listen(listen_id, backlog);
// 5. 等待连接请求 (循环处理事件)
struct rdma_cm_event *event;
rdma_get_cm_event(ec, &event);
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
struct rdma_cm_id *conn_id = event->id; // 新连接ID
// 6. 创建 QP、注册内存、接受连接
ibv_create_qp(...); // 创建 QP
rdma_accept(conn_id, ¶m); // 接受连接
}
rdma_ack_cm_event(event); // 必须确认事件
// 7. 数据传输 (通过 conn_id->qp 使用 libibverbs)
// ...
// 8. 断开连接
rdma_disconnect(conn_id);
rdma_destroy_id(conn_id);
rdma_destroy_id(listen_id);
rdma_destroy_event_channel(ec);2.2 客户端工作流程
// 1. 创建事件通道
struct rdma_event_channel *ec = rdma_create_event_channel();
// 2. 创建 CM ID
struct rdma_cm_id *conn_id;
rdma_create_id(ec, &conn_id, NULL, RDMA_PS_TCP);
// 3. 解析地址 (可选,也可直接连接)
struct rdma_addrinfo *res;
rdma_getaddrinfo("server_ip", "port", NULL, &res);
// 4. 建立连接
rdma_resolve_addr(conn_id, NULL, res->ai_dst_addr, timeout); // 解析地址
// 等待 RDMA_CM_EVENT_ADDR_RESOLVED 事件
rdma_resolve_route(conn_id, timeout); // 解析路由
// 等待 RDMA_CM_EVENT_ROUTE_RESOLVED 事件
rdma_connect(conn_id, &conn_param); // 发送连接请求
// 等待 RDMA_CM_EVENT_ESTABLISHED 事件
// 5. 数据传输
// ...
// 6. 断开连接
rdma_disconnect(conn_id);
rdma_destroy_id(conn_id);
rdma_destroy_event_channel(ec);2.3 关键事件类型


三、核心 API 详解
3.1 连接管理 API
// 创建/销毁事件通道
struct rdma_event_channel *rdma_create_event_channel(void);
void rdma_destroy_event_channel(struct rdma_event_channel *channel);
// 创建/销毁 CM ID
int rdma_create_id(struct rdma_event_channel *channel,
struct rdma_cm_id **id,
void *context,
enum rdma_port_space ps); // RDMA_PS_TCP / RDMA_PS_UDP
void rdma_destroy_id(struct rdma_cm_id *id);
// 地址绑定与解析
int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr);
int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
struct sockaddr *dst_addr, int timeout_ms);
int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms);
// 监听/连接
int rdma_listen(struct rdma_cm_id *id, int backlog);
int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param);
int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param);
int rdma_reject(struct rdma_cm_id *id, const void *private_data, uint8_t private_data_len);
int rdma_disconnect(struct rdma_cm_id *id);3.2 事件处理 API
// 获取/确认事件 (阻塞式)
int rdma_get_cm_event(struct rdma_event_channel *channel,
struct rdma_cm_event **event);
// 必须调用以释放事件资源
int rdma_ack_cm_event(struct rdma_cm_event *event);
// 事件结构体关键字段
struct rdma_cm_event {
enum rdma_cm_event_type event; // 事件类型
int status; // 状态码 (0=成功)
struct rdma_cm_id *id; // 关联的 CM ID
void *context; // 用户上下文
union {
struct rdma_conn_param conn; // 连接参数
struct rdma_ud_param ud; // UD 服务参数
} param;
};3.3 数据传输准备 API
// 创建 QP (通常在收到连接请求后或地址解析后)
struct ibv_qp *ibv_create_qp(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr);
// 注册内存区域
struct ibv_mr *ibv_reg_mr(struct ibv_pd *pd, void *addr,
size_t length, int access);
// 创建完成队列
struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,
void *cq_context, struct ibv_comp_channel *channel,
int comp_vector);四、完整使用示例
4.1 服务端示例
SW arch

#include <rdma/rdma_cma.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEST_NZ(x) do { if ( (x)) die("error: " #x " failed."); } while (0)
#define TEST_Z(x) do { if (!(x)) die("error: " #x " failed."); } while (0)
void die(const char *reason) {
fprintf(stderr, "%s\n", reason);
exit(EXIT_FAILURE);
}
// 连接上下文
struct conn_context {
struct ibv_qp *qp;
struct ibv_mr *mr;
char *buf;
};
void on_connect_request(struct rdma_cm_id *id) {
struct conn_context *ctx = calloc(1, sizeof(struct conn_context));
// 1. 创建保护域
struct ibv_pd *pd = ibv_alloc_pd(id->verbs);
TEST_Z(pd);
// 2. 创建完成队列
struct ibv_cq *cq = ibv_create_cq(id->verbs, 10, NULL, NULL, 0);
TEST_Z(cq);
// 3. 创建 QP
struct ibv_qp_init_attr qp_attr = {
.send_cq = cq,
.recv_cq = cq,
.qp_type = IBV_QPT_RC, // Reliable Connection
.cap = {
.max_send_wr = 10,
.max_recv_wr = 10,
.max_send_sge = 1,
.max_recv_sge = 1
}
};
TEST_NZ(rdma_create_qp(id, pd, &qp_attr));
// 4. 注册内存
ctx->buf = malloc(256);
ctx->mr = ibv_reg_mr(pd, ctx->buf, 256,
IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ);
TEST_Z(ctx->mr);
id->context = ctx;
// 5. 接受连接
struct rdma_conn_param cm_params = {0};
TEST_NZ(rdma_accept(id, &cm_params));
printf("Connection accepted\n");
}
void on_disconnect(struct rdma_cm_id *id) {
struct conn_context *ctx = id->context;
rdma_destroy_qp(id);
ibv_dereg_mr(ctx->mr);
free(ctx->buf);
free(ctx);
printf("Connection closed\n");
}
int main(int argc, char **argv) {
struct rdma_event_channel *ec = rdma_create_event_channel();
TEST_Z(ec);
struct rdma_cm_id *listener;
TEST_NZ(rdma_create_id(ec, &listener, NULL, RDMA_PS_TCP));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(12345),
.sin_addr.s_addr = INADDR_ANY
};
TEST_NZ(rdma_bind_addr(listener, (struct sockaddr *)&addr));
TEST_NZ(rdma_listen(listener, 10));
printf("Listening on port 12345...\n");
struct rdma_cm_event *event;
while (rdma_get_cm_event(ec, &event) == 0) {
struct rdma_cm_id *id = event->id;
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
on_connect_request(id);
break;
case RDMA_CM_EVENT_ESTABLISHED:
printf("Connection established\n");
break;
case RDMA_CM_EVENT_DISCONNECTED:
on_disconnect(id);
break;
default:
printf("Event: %s\n", rdma_event_str(event->event));
break;
}
rdma_ack_cm_event(event); // 必须确认!
}
rdma_destroy_id(listener);
rdma_destroy_event_channel(ec);
return 0;
}4.2 客户端示例
#include <rdma/rdma_cma.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEST_NZ(x) do { if ( (x)) die("error: " #x " failed."); } while (0)
#define TEST_Z(x) do { if (!(x)) die("error: " #x " failed."); } while (0)
void die(const char *reason) {
fprintf(stderr, "%s\n", reason);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <server_ip>\n", argv[0]);
return 1;
}
struct rdma_event_channel *ec = rdma_create_event_channel();
TEST_Z(ec);
struct rdma_cm_id *conn_id;
TEST_NZ(rdma_create_id(ec, &conn_id, NULL, RDMA_PS_TCP));
// 解析服务器地址
struct rdma_addrinfo *res;
TEST_NZ(rdma_getaddrinfo(argv[1], "12345", NULL, &res));
// 解析地址
TEST_NZ(rdma_resolve_addr(conn_id, NULL, res->ai_dst_addr, 2000));
struct rdma_cm_event *event;
while (rdma_get_cm_event(ec, &event) == 0) {
if (event->event == RDMA_CM_EVENT_ADDR_RESOLVED) {
printf("Address resolved\n");
rdma_resolve_route(conn_id, 2000);
} else if (event->event == RDMA_CM_EVENT_ROUTE_RESOLVED) {
printf("Route resolved\n");
// 创建 QP (类似服务端)
struct ibv_pd *pd = ibv_alloc_pd(conn_id->verbs);
struct ibv_cq *cq = ibv_create_cq(conn_id->verbs, 10, NULL, NULL, 0);
struct ibv_qp_init_attr qp_attr = {
.send_cq = cq, .recv_cq = cq,
.qp_type = IBV_QPT_RC,
.cap = { .max_send_wr = 10, .max_recv_wr = 10,
.max_send_sge = 1, .max_recv_sge = 1 }
};
rdma_create_qp(conn_id, pd, &qp_attr);
// 注册内存
char *buf = malloc(256);
struct ibv_mr *mr = ibv_reg_mr(pd, buf, 256,
IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ);
// 发送连接请求
struct rdma_conn_param cm_params = {0};
rdma_connect(conn_id, &cm_params);
} else if (event->event == RDMA_CM_EVENT_ESTABLISHED) {
printf("Connected! Ready for data transfer.\n");
// 这里可以进行 RDMA 操作 (Send/Recv/Read/Write)
// ...
break;
} else if (event->event == RDMA_CM_EVENT_REJECTED) {
die("Connection rejected");
}
rdma_ack_cm_event(event);
}
// 断开连接
rdma_disconnect(conn_id);
rdma_destroy_id(conn_id);
rdma_destroy_event_channel(ec);
return 0;
}五、关键使用要点
5.1 必须遵守的规则
- 1. 事件确认:每次
rdma_get_cm_event()后必须调用rdma_ack_cm_event(),否则会导致资源泄漏 - 2. QP 创建时机:服务端在
CONNECT_REQUEST事件中创建,客户端在ROUTE_RESOLVED后创建 - 3. 资源释放顺序:先
rdma_destroy_qp()→ 再ibv_dereg_mr()→ 最后rdma_destroy_id()
5.2 两种使用模式

5.3 与 libibverbs 的关系
- • librdmacm 负责:连接建立、地址解析、事件通知
- • libibverbs 负责:QP/CQ/MR 管理、实际 RDMA 操作 (post_send, post_recv, poll_cq)
- • 协同工作:librdmacm 创建的
rdma_cm_id包含ibv_qp指针,数据传输需通过 verbs API 操作5.4 编译与运行
# 编译
gcc server.c -o server -lrdmacm -libverbs
gcc client.c -o client -lrdmacm -libverbs
# 运行 (需要 RDMA 网卡,如 Mellanox ConnectX 系列)
./server
./client <server_ip>六、总结
librdmacm 简化了 RDMA 编程中最复杂的连接管理部分,其核心优势在于:
- 1. 抽象连接流程:将复杂的 RDMA 连接建立过程抽象为事件驱动模型
- 2. 统一地址解析:支持 IP 地址和 RDMA 特定地址的解析
- 3. 与 verbs 解耦:连接管理独立于数据传输,便于分层设计


夜雨聆风