以下是一个使用TCP的RDMA实现的示例代码。该代码使用了OFED和libibverbs库,需要在编译前安装这些依赖项。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rdma/rdma_cma.h>
#define BUF_SIZE 4096
struct rdma_conn {
struct ibv_context *context;
struct rdma_cm_id *id;
struct ibv_pd *pd;
struct ibv_comp_channel *channel;
struct ibv_cq *cq;
struct ibv_qp_init_attr qp_init_attr;
struct ibv_qp *qp;
int connected;
};
void on_completion(struct ibv_wc *wc);
int post_receive(struct rdma_conn *conn);
int send_data(struct rdma_conn *conn, char *buf, size_t size);
int main(int argc, char **argv)
{
if (argc != 3) {
printf("Usage: %s [server_ip] [server_port]\n", argv[0]);
return -1;
}
const char* server_ip = argv[1];
const uint16_t server_port = atoi(argv[2]);
// initialize RDMA resources
struct rdma_conn conn = {0};
// create context
conn.context = ibv_open_device();
// create protection domain
conn.pd = ibv_alloc_pd(conn.context);
// create completion channel
conn.channel = ibv_create_comp_channel(conn.context);
// create completion queue
conn.cq = ibv_create_cq(conn.context, 10, NULL, conn.channel, 0);
// create QP init attributes
memset(&conn.qp_init_attr, 0, sizeof(conn.qp_init_attr));
conn.qp_init_attr.send_cq = conn.cq;
conn.qp_init_attr.recv_cq = conn.cq;
conn.qp_init_attr.cap.max_send_wr = 10;
conn.qp_init_attr.cap.max_recv_wr = 10;
conn.qp_init_attr.cap.max_send_sge = 1;
conn.qp_init_attr.cap.max_recv_sge = 1;
conn.qp_init_attr.cap.max_inline_data = BUF_SIZE;
// create QP
int ret = rdma_create_qp(NULL, conn.pd, &conn.qp_init_attr);
if (ret) {
printf("rdma_create_qp failed: %d\n", ret);
return -1;
}
conn.qp = ret;
// resolve destination address
struct sockaddr_in sin = {0};
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(server_ip);
sin.sin_port = htons(server_port);
struct rdma_addrinfo hints = {0};
hints.ai_flags |= RAI_PASSIVE;
hints.ai_port_space = RDMA_PS_TCP;
struct rdma_addrinfo *rai_list, *rai_ptr;
ret = rdma_getaddrinfo((char*)server_ip, NULL, &hints, &rai_list);
if (ret) {
printf("rdma_getaddrinfo error: %d\n", ret);
return -1;
}
for (rai_ptr=rai_list; rai_ptr!=NULL; rai_ptr=rai_ptr->ai_next) {
ret = rdma_resolve_addr(conn.id, NULL,
rai_ptr->ai_dst_addr,
RDMA_DEFAULT_TIMEOUT_MS);
if (!ret) break;
}
rdma_freeaddrinfo(rai_list);
if (ret) {
printf("rdma_resolve_addr error: %d\n", ret);
return -1;
}
// resolve route
ret = rdma_resolve_route(conn.id, RDMA_DEFAULT_TIMEOUT_MS);
if (ret) {
printf("rdma_resolve_route error: %d\n", ret);
return -1;
}
// wait for connection
struct rdma_cm_event *event;
ret = rdma_get_cm_event(conn.channel, &event);
if (ret) {
printf("rdma_get_cm_event failed: %d\n", ret);
return -1;
}
rdma_ack_cm_event(event);
if (event->event != RDMA_CM_EVENT_ESTABLISHED) {
printf("Unexpected event: %s (%d)\n",
rdma_event_str(event->event), event->event);
return -1;
}
conn.connected = 1;
// post receive operations
char recv_buf[BUF_SIZE] = {0};
struct ibv_mr *recv_mr = ibv_reg_mr(conn.pd, recv_buf, BUF_SIZE,
IBV_ACCESS_LOCAL_WRITE |
IBV_ACCESS_REMOTE_WRITE |
IBV_ACCESS_REMOTE_READ);
if (!recv_mr) {
printf("ibv_reg_mr failed\n");
return -1;
}
for (int i=0; i<10; ++i) {
post_receive(&conn);
}
// send data
char* send_buf = "Hello RDMA!";
size_t send_size = strlen(send_buf)+1;
send_data(&conn, send_buf, send_size);
printf("[INFO] Data sent: %s\n", send_buf);
sleep(3);
// cleanup
rdma_disconnect(conn.id);
rdma_destroy_qp(conn.id);
rdma_destroy_id(conn.id);
ibv_dereg_mr(recv_mr);
ibv_destroy_qp(conn.qp);
ibv_destroy_cq(conn.cq);
ibv_destroy_comp_channel(conn.channel);
ibv_dealloc_pd(conn.pd);
ibv_close_device(conn.context);
return 0;
}
void on_completion(struct ibv_wc *wc)
{
printf("[INFO] Operation completed: %s (%d)\n",
ibv_wc_status_str(wc->status), wc->status);
if (wc->opcode & IBV_WC_RECV) {
printf("[INFO] Received data: %s\n", (char*)wc->wr_id);
post_receive((struct rdma_conn*)wc->wr_id);
}
}
int post_receive(struct rdma_conn *conn)
{
struct ibv_sge sge = {0};
sge.addr = (uintptr_t)malloc(BUF_SIZE);
sge.length = BUF_SIZE;
sge.lkey = ibv_reg_mr(conn->pd, sge.addr, sge.length,
IBV_ACCESS_LOCAL_WRITE |
IBV_ACCESS_REMOTE_WRITE |
IBV_ACCESS_REMOTE_READ)->lkey;
struct ibv_recv_wr wr = {0};
wr.sg_list = &sge;
wr.num_sge = 1;
struct ibv_recv_wr *bad_wr;
int ret = ibv_post_recv(conn->qp, &wr, &bad_wr);
if (ret) {
printf("ibv_post_recv failed: %d\n", ret);
return -1;
}
return 0;
}
int send_data(struct rdma_conn *conn, char *buf, size_t size)
{
struct ibv_sge sglist[2] = {0};
struct ibv_send_wr wr = {0};
char* send_buf = malloc(size + 1);
memcpy(send_buf, buf, size+1);
sglist[0].addr = (uintptr_t)send_buf;
sglist[0].length = size + 1;
sglist[0].lkey = ibv_reg_mr(conn->pd, send_buf,
sglist[0].length,
IBV_ACCESS_LOCAL_WRITE |
IBV_ACCESS_REMOTE_READ)->lkey;
wr.sg_list = sglist;
wr.num_sge = 1;
wr.opcode = IBV_WR_SEND;
wr.send_flags = IBV_SEND_SIGNALED;
struct ibv_send_wr *bad_wr;
int ret = ibv_post_send(conn->qp, &wr, &bad_wr);
if (ret) {
printf("ibv_post_send failed: %d\n", ret);
return -1;
}
return 0;
}
该代码实现了一个客户端程序,连接到指定的服务器,并通过RDMA协议发送数据。具体来说,它首先创建了RDMA资源(包括上下文、保护域、完成通道、完成队列和QP等),然后解析远程地址并建立连接。在连接建立之后,它向服务器发送一条消息,并等待回复。最后,它清理所有资源并退出程序。
需要注意的是,在使用RDMA协议时需要仔细管理内存区域和注册的MR(内存区域)对象。另外,在这个例子中我们只实现了基本的发送和接收操作,实际应用中还需要考虑更多因素(例如错误处理、流量控制等)。
内容由零声教学AI助手提供,问题来源于学员提问