赞
踩
#RDMA编程 建立连接
##1. RDMA的学习环境搭建
RDMA需要专门的RDMA网卡或者InfiniBand卡才能使用,学习RDMA而又没有这些硬件设备,可以使用一个软件RDMA模拟环境,softiwarp ,
这里也有一个RDMA编程的入门示例,
##2. RDMA与socket的类比
和Socket连接类似,RDMA连接也分为可靠连接和不可靠连接。然而也不完全相同,Socket的可靠连接就是TCP连接,是流式的;不可靠连接也就是UDP,是消息式的。对于RDMA来说,无论是可靠连接和不可靠连接,都是消息式的。
编程角度看,RDMA代码也分为Server端,Client端,也有bind, listen, connect, accept,等动作,然而细节上仍有不少区别。
##3. 在Server端,一个RDMA服务器的代码流程如下:
rdma_create_event_channel
这一步是创建一个event channel,event channel是RDMA设备在操作完成后,或者有连接请求等事件发生时,用来通知应用程序的通道。其内部就是一个file descriptor, 因此可以进行poll等操作。
rdma_create_id
这一步创建一个rdma_cm_id, 概念上等价与socket编程时的listen socket。
rdma_bind_addr
和socket编程一样,也要先绑定一个本地的地址和端口,以进行listen操作。
rdma_listen
开始侦听客户端的连接请求
rdma_get_cm_event
这个调用就是作用在第一步创建的event channel上面,要从event channel中获取一个事件。这是个阻塞调用,只有有事件时才会返回。在一切正常的情况下,函数返回时会得到一个 RDMA_CM_EVENT_CONNECT_REQUEST事件,也就是说,有客户端发起连接了。
在事件的参数里面,会有一个新的rdma_cm_id传入。这点和socket是不同的,socket只有在accept后才有新的socket fd创建。
ibv_alloc_pd
创建一个protection domain。protection domain可以看作是一个内存保护单位,在内存区域和队列直接建立一个关联关系,防止未授权的访问。
ibv_create_comp_channel
和之前创建的event channel类似,这也是一个event channel,但只用来报告完成队列里面的事件。当完成队列里有新的任务完成时,就通过这个channel向应用程序报告。
ibv_create_cq
创建完成队列,创建时就指定使用第6步的channel。
rdma_create_qp
创建一个queue pair, 一个queue pair包括一个发送queue和一个接收queue. 指定使用前面创建的cq作为完成队列。该qp创建时就指定关联到第6步创建的pd上。
ibv_reg_mr
注册内存区域。RDMA使用的内存,必须事先进行注册。这个是可以理解的,DMA的内存在边界对齐,能否被swap等方面,都有要求。
rdma_accept
至此,做好了全部的准备工作,可以调用accept接受客户端的这个请求了。 --:)长出一口气 ~~ 且慢,
rdma_ack_cm_event
对于每个从event channel得到的事件,都要调用ack函数,否则会产生内存泄漏。这一步的ack是对应第5步的get。每一次get调用,都要有对应的ack调用。
rdma_get_cm_event
继续调用rdma_get_cm_event
, 一切正常的话我们此时应该得到 RDMA_CM_EVENT_ESTABLISHED 事件,表示连接已经建立起来。不需要做额外的处理,直接rdma_ack_cm_event
就行了
终于可以开始进行数据传输了 ==== (如何传输下篇再说)
##4. 关闭连接
断开连接
当rdma_get_cm_event
返回RDMA_CM_EVENT_DISCONNECTED事件时,表示客户端断开了连接,server端要进行对应的清理。此时可以调用rdma_ack_cm_event
释放事件资源。然后依次调用下面的函数,释放连接资源,内存资源,队列资源。
rdma_disconnect
rdma_destroy_qp
ibv_dereg_mr
rdma_destroy_id
释放同客户端连接的rdma_cm_id
rdma_destroy_id
释放用于侦听的rdma_cm_id
rdma_destroy_event_channel
释放 event channel
最后来show一下code吧, 这是server端的代码,client端的代码在本人的另外一篇:
//compile : gcc rdma-srv.cpp -libverbs -lrdmacm #define _ISOC11_SOURCE #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <infiniband/verbs.h> #include <rdma/rdma_cma.h> struct rdma_event_channel* ec; struct rdma_cm_id* cm_id; struct ibv_pd* pd; struct ibv_context* rdma_cm_context; void* recv_buf; #define RECV_BUF_SIZE 4096 struct ibv_mr* recv_mr; int on_connect_request(struct rdma_cm_event* evt) { int rc = 0; struct rdma_conn_param cm_params; struct rdma_cm_id* id = evt->id; //conn = new PfRdmaConnection(); //conn->dev_ctx = build_context(id->verbs); struct ibv_context* rdma_context = id->verbs; pd = ibv_alloc_pd(rdma_context); recv_buf = aligned_alloc(4096, RECV_BUF_SIZE); recv_mr = ibv_reg_mr(pd, recv_buf, RECV_BUF_SIZE, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ); struct ibv_comp_channel* comp_channel = ibv_create_comp_channel(rdma_context); struct ibv_cq* cq = ibv_create_cq(rdma_context, 512, NULL, comp_channel, 0); ibv_req_notify_cq(cq, 0); struct ibv_qp_init_attr qp_attr={0}; qp_attr.send_cq = cq; qp_attr.recv_cq = cq; qp_attr.qp_type = IBV_QPT_RC; qp_attr.cap.max_send_wr = 512; qp_attr.cap.max_recv_wr = 512; qp_attr.cap.max_send_sge = 1; qp_attr.cap.max_recv_sge = 1; rc = rdma_create_qp(id, pd, &qp_attr); if(rc) { perror("rdma_create_qp, errno:%d"); return rc; } //id->context = conn; struct ibv_recv_wr wr, *bad_wr = NULL; struct ibv_sge sge; wr.wr_id = (uint64_t)1; wr.next = NULL; wr.sg_list = &sge; wr.num_sge = 1; sge.addr = (uint64_t)recv_buf; sge.length = RECV_BUF_SIZE; sge.lkey = recv_mr->lkey; rc = ibv_post_recv(id->qp, &wr, &bad_wr); if (rc != 0) { perror("ibv_post_recv failed, "); return rc; } memset(&cm_params, 0, sizeof(cm_params)); cm_params.responder_resources = (uint8_t)16; cm_params.initiator_depth = (uint8_t)16; cm_params.retry_count = 7; cm_params.rnr_retry_count = 7; rc = rdma_accept(id, &cm_params); if (rc) { perror("rdma_accept failed, "); return rc; } struct sockaddr * peer_addr = rdma_get_peer_addr(id); char ip_str[64]; inet_ntop(AF_INET, &(((struct sockaddr_in *)peer_addr)->sin_addr),ip_str, sizeof(ip_str)); printf("rdma_accepted succeed, from peer:%s \n", ip_str); struct ibv_wc wc[8]; void *cq_ctx; int n; ibv_get_cq_event(comp_channel, &cq, &cq_ctx); ibv_ack_cq_events(cq, 1); ibv_req_notify_cq(cq, 0); printf("Begin polling completion queue ...\n"); while((n = ibv_poll_cq(cq, 8, wc))) { for(int i=0; i<n; i++) { long user_data = wc[i].wr_id; assert(user_data == 1); if(wc[i].status != IBV_WC_SUCCESS){ fprintf(stderr, "wc[%d].status != IBV_WC_SUCCESS, wc.status:%d, %s", i, wc[i].status, ibv_wc_status_str(wc[i].status)); return 1; } printf("Receive:%d bytes, '%.*s'\n", wc[i].byte_len, wc[i].byte_len, recv_buf); } } return 0; release0: rdma_destroy_qp(id); return rc; } int main(int argc, char** argv) { int rc; int port=10121; struct sockaddr_in listen_addr; memset(&listen_addr, 0, sizeof(listen_addr)); listen_addr.sin_family = AF_INET; listen_addr.sin_port = htons((uint16_t)port); ec = rdma_create_event_channel(); if(ec == NULL){ perror("Failed create event channel"); return errno; } rc = rdma_create_id(ec, &cm_id, NULL, RDMA_PS_TCP); if(rc) { perror("rdma_create_id"); return rc; } rdma_cm_context = cm_id->verbs; rc = rdma_bind_addr(cm_id, (struct sockaddr*)&listen_addr); if(rc) { perror("rdma_bind_addr"); return rc; } rc = rdma_listen(cm_id, 10); if(rc) { perror("rdma_listen"); return rc; } struct rdma_cm_event* event = NULL; printf("Waiting cm evnet ...\n"); while(rdma_get_cm_event(ec, &event)==0) { struct rdma_cm_event event_copy; memcpy(&event_copy, event, sizeof(*event)); printf("Get cm evnet %d(%s)...\n", event_copy.event, rdma_event_str (event_copy.event)); rdma_ack_cm_event(event); if(event_copy.event == RDMA_CM_EVENT_CONNECT_REQUEST) { on_connect_request(&event_copy); } } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。