RDMA编程-2 client端流程

在上一篇建立RDMA侦听 部分讲了RDMA 通信时server端需要进行的操作,这一篇我们来说一下客户端的流程。

##client端流程
RDMA编程时的client端和socket编程也是大体类似的,只是有一些跟设备相关的细节,包括地址解析,queue pair, memory region 等要自己显式处理。

  1. rdma_getaddrinfo
    获取服务器的地址信息
  2. rdma_create_event_channel
    create channel to receive events
  3. rdma_create_id
    allocate an rdma_cm_id, this is conceptually similar to a socket
  4. rdma_resolve_addr
    obtain a local RDMA device to reach the remote address
  5. rdma_get_cm_event
    wait for RDMA_CM_EVENT_ADDR_RESOLVED event
  6. rdma_ack_cm_event
    ack event
  7. rdma_create_qp
    allocate a QP for the communication
  8. rdma_resolve_route
    determine the route to the remote address
  9. rdma_get_cm_event
    wait for RDMA_CM_EVENT_ROUTE_RESOLVED event
  10. rdma_ack_cm_event
    ack event
  11. rdma_connect
    connect to the remote server
  12. rdma_get_cm_event
    wait for RDMA_CM_EVENT_ESTABLISHED event
  13. rdma_ack_cm_event
    ack event
  14. ibv_post_send
    Perform data transfers over connection
  15. rdma_disconnect
    tear-down connection
  16. rdma_get_cm_event
    wait for RDMA_CM_EVENT_DISCONNECTED event
  17. rdma_ack_cm_event
    ack event
  18. rdma_destroy_qp
    destroy the QP
  19. rdma_destroy_id
    release the rdma_cm_id
  20. rdma_destroy_event_channel
    release the event channel

下面是一个完整的client代码,这个代码可以配合建立RDMA侦听 里的server端代码进行测试。

测试时需要注意,不要使用使用localhost/127.0.0.1这样的回环地址去连接server端,回环地址会绕过RDMA网卡,因此无法进行RDMA通信。

//compile :  gcc rdma-client.cpp -libverbs -lrdmacm -o rdma-client

#define _ISOC11_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <infiniband/verbs.h>
#include <rdma/rdma_cma.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.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_route_resolved(struct rdma_cm_id* id)
{
	int rc = 0;
	struct rdma_conn_param cm_params;
	struct ibv_qp_init_attr qp_attr={0};
	struct PfRdmaConnection* conn = (struct PfRdmaConnection*)id->context;
	
	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_WRITE);	
	
	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);


	
    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("create_qp failed, errno:%d");
        return rc;
	}
	

	memset(&cm_params, 0, sizeof(cm_params));

	cm_params.private_data = NULL;
	cm_params.private_data_len = 0;
	cm_params.responder_resources = (uint8_t)8;
	cm_params.initiator_depth = (uint8_t)8;
	cm_params.retry_count = 7;
	cm_params.rnr_retry_count = 7;
	rc = rdma_connect(id, &cm_params);
	if(rc)
	{
		perror("rdma_connect failed, errno");
		return rc;
	}
	return 0;
}




int main(int argc, char** argv)
{
	
	int port=10121;
	
	
	int rc = 0;
	if(argc != 2){
		fprintf(stderr, "Usage: rdma-client <server_ip>\n");
		return 1;
	}
	char* ip_str=argv[1];
	struct addrinfo* addr;
	rc = getaddrinfo(ip_str, NULL, NULL, &addr);
	if(rc)
	{
		perror("getaddrinfo failed, ");
		return rc;
	}
	((struct sockaddr_in*)addr->ai_addr)->sin_port = htons((uint16_t)port);
	printf("connecting to server ip:%s...\n", ip_str);
	
	ec = rdma_create_event_channel();
	if(ec == NULL)
	{
		perror("rdma_create_event_channel failed, errno:");
		return errno;
	}
	rc = rdma_create_id(ec, &cm_id, NULL, RDMA_PS_TCP);
	if(rc)
	{
		perror("rdma_create_id failed, errno");
		return rc;
	}
	
	rc = rdma_resolve_addr(cm_id, NULL, addr->ai_addr, 500);
	if(rc)
	{
		perror("rdma_resolve_addr failed, errno:");
		return rc;
	}
	
	printf("Begin process cm event...\n");
	struct rdma_cm_event *event = NULL;
	while(rdma_get_cm_event(ec, &event) == 0) {
		struct rdma_cm_event event_copy;
		memcpy(&event_copy, event, sizeof(*event));
		rdma_ack_cm_event(event);
		switch(event_copy.event)
		{
			case RDMA_CM_EVENT_ADDR_RESOLVED:
				printf("get event RDMA_CM_EVENT_ADDR_RESOLVED\n");
				rc = rdma_resolve_route(event_copy.id, 2000);
				if(rc)
				{
					perror("rdma_resolve_route failed, errno:");
					return rc;
				}
				break;
			case RDMA_CM_EVENT_ROUTE_RESOLVED:
				printf("get event RDMA_CM_EVENT_ROUTE_RESOLVED\n");
				on_route_resolved(event_copy.id);
				break;
			case RDMA_CM_EVENT_ESTABLISHED:
				printf("get event RDMA_CM_EVENT_ESTABLISHED\n");
				{
					struct ibv_send_wr wr, *bad_wr = NULL;
					struct ibv_sge sge;


					memset(&wr, 0, sizeof(wr));
					wr.wr_id = (uint64_t)1;
					wr.next = NULL;
					wr.sg_list = &sge;
					wr.num_sge = 1;
					wr.opcode = IBV_WR_SEND;
					wr.send_flags = IBV_SEND_SIGNALED;
					strcpy((char*)recv_buf, "HelloWorld"); 
					sge.addr = (uint64_t)recv_buf;
					sge.length = strlen((char*)recv_buf);
					sge.lkey = recv_mr->lkey;
					printf("send data to server\n");
					rc = ibv_post_send(cm_id->qp, &wr, &bad_wr);
					if (rc)
					{
						fprintf(stderr, "ibv_post_send failed, rc:%d\n", rc);
						return rc;
					}
					return 0;
				}
				break;
			case RDMA_CM_EVENT_DISCONNECTED:
				printf("get event RDMA_CM_EVENT_DISCONNECTED\n");
				
				break;
			default:
				break;
		}
	}
	freeaddrinfo(addr);
	return 0;
}


通过这两篇文章,读者应该可以快速的建立一个自己的RDMA通信程序了。在上面的例子里面,server端使用了IBV_RECEIVE操作,client端使用了IBV_SEND操作。这个意义上还是和socket编程很像的。虽然这样操作也避免了类似TCP协议栈在CPU上的开销,但这远不是RDMA的精髓。RDMA总共提供了4个操作,分成两类:

  1. 双边API (two-sided), 这类API的操作需要server, client双方配合,需要一边先post_recv, 然后另外一边post_send,两个操作完成一次传输。这类操作就是我们例子里用到的
    • IBV_SEND
    • IBV_RECEIVE
  2. 单边API (one-sided), 这类API只需要server或者client一边调用API,就可以从另一边的内存里面读写数据。包括
    • IBV_READ
    • IBV_WRITE

要想用好RDMA, 就要将这两类API配合使用好。往往需要结合业务设计出最合适的协议模型。也欢迎大家到我的开源存储项目PureFlash ,这是一个RDMA在实际项目应用的具体例子。

### 回答1: RDMA编程用户手册-官方中文版,是一个介绍RDMA(Remote Direct Memory Access,远程直接内存访问)编程的指南,全文共分为六章,内容详细而清晰。该手册引导读者从熟悉RDMA的基本概念开始,到理解和使用RDMA编程模型,最后为读者提供了一些高级主题,如优化数据传输、多资源管理等。 第一章介绍了RDMA及其相关概念,如IB(InfiniBand,无穷带宽)和RoCE(RDMA over Converged Ethernet,以太网上的RDMA),使读者对RDMA有了初步了解。 第二章讲述了RDMA编程模型及其基本特性,如零拷贝、CPU减轻、低延迟、高吞吐量等。该章还介绍了RDMA的三种通信方式:发送/接收(send/receive)、发送/写(send/write)和原子操作(atomic operations)。 第三章详细介绍了RDMA编程中的一些重要概念,如信号量、内存区域、点和队列,为读者进一步了解RDMA编程模型打下基础。 第四章详细介绍了RDMA编程接口(APIs),包括IB Verbs(IB词汇)和UCP(Unified Communication Platform,统一通信平台),并提供了相关示例代码和解释。 第五章介绍了RDMA应用的一些高级话题,如数据传输优化、内存区域与队列管理、事件处理等,提供了进一步优化RDMA应用的方法和技巧。 最后一章通过具体的案例分享了RDMA编程的示例,从简单的ping-pong测试到复杂的数据传输、内存区域管理、事件处理等,为读者提供了实际应用经验和使用技巧。 总之,这个手册是一个非常有用的资源,不仅对初学者具有参考价值,也为专业RDMA编程人员提供了实用信息和技巧。 ### 回答2: RDMA 编程用户手册 (官方中文版) 是一份非常详细的技术文档,主要面向使用 RDMA 开发网络应用程序的开发人员。该手册包括了 RDMA 的基本介绍、RDMA 技术的优点、RDMA 编程的基本原理和方法、RDMA 常见编程模式、RDMA 应用编程界面、RDMA 编程工具等内容。通过这份手册,读者可以了解 RDMA 技术的基础知识、掌握 RDMA 编程方法和技巧,更好地开发和优化基于 RDMA 的网络应用程序。 手册首先介绍了 RDMA 的基础概念、优点和实现原理,为读者提供了深入理解 RDMA 技术的基础知识。接着,手册详细介绍了 RDMA 技术的编程方法和基本模式,包括点对点 RDMA、远程读写、原子操作等,给读者提供了开发 RDMA 应用程序的基本指南。同时,手册还介绍了 RDMA API,为读者提供了详细的接口说明和使用方法。此外,手册还介绍了 RDMA 编程工具和调试技巧,方便读者对 RDMA 应用程序进行优化和调试。 总之,RDMA 编程用户手册 (官方中文版) 是一份非常有用的技术文档,对于需要开发和优化基于 RDMA 的网络应用程序的开发人员来说,是一份必备的工具和参考资料。 ### 回答3: RDMA(Remote Direct Memory Access)是一种异步、零拷贝数据传输技术,它允许网络主机直接访问远主机的内存。RDMA编程用户手册是RDMA编程的权威指南,对于RDMA编程掌握和实践意义重大。 该手册被分为三个主要部分,分别是RDMA概述、RDMA编程和RDMA应用,对RDMA编程的基础知识、源代码实现以及应用领域进行了详细描述和讲解。其中,RDMA概述主要介绍RDMA的发展历程、基本原理、硬件支持和软件实现等,为读者提供了深入了解RDMA的基础知识。RDMA编程部分则主要介绍了RDMA编程的基本模型、原子操作、数据类型和粘包处理等,同时提供了丰富的代码实现和案例分析,以方便读者进行实践活动。RDMA应用部分主要讲解RDMA在各种场景中的应用,包括存储系统、网络加速、云计算和高性能计算等,帮助读者了解RDMA技术在各种实际应用领域中的表现和优势。 总体而言,RDMA编程用户手册-官方中文版是一本介绍RDMA编程的权威指南,对于打算了解和掌握RDMA编程技术的人员具有重要意义。该手册不仅提供了丰富的知识资源和代码实现支持,而且分析了RDMA技术在各种场景中的应用场景和优点,为读者掌握RDMA编程技术和加速应用提供了有力支持和指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值