📅  最后修改于: 2023-12-03 14:57:57.470000             🧑  作者: Mango
远程直接内存访问(RDMA)是一种网络协议,在高性能计算(HPC)环境中被广泛使用,以实现高吞吐量和低延迟的数据传输。RDMA使得计算机节点可以在不涉及CPU的情况下直接访问远程计算机节点的内存,从而避免了数据传输过程中CPU的介入,提高了数据传输的效率。
RDMA的实现依赖于底层网络技术,如InfiniBand、RoCE和iWARP等,这些技术提供了低延迟和高速的网络通信方式,从而实现了RDMA协议。
RDMA技术与传统TCP/IP协议的数据传输方式相比有如下优点:
RDMA技术在数据中心、云计算和HPC等领域得到广泛应用。以下是RDMA技术的主要应用场景:
RDMA编程模型分为两种:
基于RDMA通信涉及的RDMA驱动和RDMA硬件。这种模型需要了解RDMA硬件和驱动的细节,以及RDMA API的使用方式,从而实现高效的数据传输。
基于Verbs API的RDMA编程。RDMA Verbs API是一个抽象层,隐藏了底层的RDMA设备和驱动细节,简化了程序员的开发,提高了开发效率。在这种模型下,程序员只需要了解Verbs API的使用方式即可进行RDMA编程。
RDMA技术需要操作系统的支持,Linux内核从2.6.27版本开始支持RDMA技术。用户可以通过安装RDMA软件包来使用RDMA技术。
以下是使用RDMA技术进行数据传输的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rdma/rdma_cma.h>
#define BUFFER_SIZE 1024
struct rdma_context {
struct rdma_cm_id *id;
struct rdma_event_channel *ec;
struct ibv_pd *pd;
struct ibv_cq *cq;
struct ibv_mr *mr;
char *buffer;
unsigned int buffer_size;
char *remote_buffer;
unsigned int remote_buffer_size;
};
void register_memory(struct rdma_context *ctx)
{
ctx->mr = ibv_reg_mr(ctx->pd,
ctx->buffer,
ctx->buffer_size,
IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);
if (!ctx->mr) {
fprintf(stderr, "ibv_reg_mr failed\n");
exit(EXIT_FAILURE);
}
}
void build_context(struct rdma_context *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->buffer_size = BUFFER_SIZE;
ctx->buffer = malloc(ctx->buffer_size);
if (!ctx->buffer) {
fprintf(stderr, "failed to allocate buffer\n");
exit(EXIT_FAILURE);
}
register_memory(ctx);
ctx->remote_buffer_size = BUFFER_SIZE;
ctx->remote_buffer = malloc(ctx->remote_buffer_size);
if (!ctx->remote_buffer) {
fprintf(stderr, "failed to allocate remote buffer\n");
exit(EXIT_FAILURE);
}
}
void destroy_context(struct rdma_context *ctx)
{
ibv_dereg_mr(ctx->mr);
free(ctx->buffer);
free(ctx->remote_buffer);
rdma_destroy_event_channel(ctx->ec);
rdma_destroy_id(ctx->id);
}
void on_connect(void *context)
{
struct rdma_cm_id *id = (struct rdma_cm_id *)context;
struct rdma_context *ctx = (struct rdma_context *)id->context;
struct ibv_send_wr wr, *bad_wr = NULL;
struct ibv_sge sge;
memset(&wr, 0, sizeof(wr));
wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
wr.send_flags = IBV_SEND_SIGNALED;
wr.imm_data = 0;
wr.wr_id = 1;
wr.wr.rdma.remote_addr = (uint64_t)ctx->remote_buffer;
wr.wr.rdma.rkey = id->context->recv_buf[2].imm_data;
sge.addr = (uint64_t)ctx->buffer;
sge.length = BUFFER_SIZE;
sge.lkey = ctx->mr->lkey;
wr.sg_list = &sge;
wr.num_sge = 1;
if (ibv_post_send(ctx->id->qp, &wr, &bad_wr)) {
fprintf(stderr, "ibv_post_send failed\n");
exit(EXIT_FAILURE);
}
}
void on_completion(struct ibv_wc *wc)
{
printf("on completion: status=%d, opcode=%d\n", wc->status, wc->opcode);
}
void build_connection(struct rdma_context *ctx)
{
struct rdma_conn_param conn_param;
struct rdma_cm_event *event;
struct rdma_cm_id *id;
struct rdma_conn_param conn_param;
struct rdma_cm_event *event;
memset(&conn_param, 0, sizeof(conn_param));
conn_param.responder_resources = 1;
conn_param.initiator_depth = 1;
conn_param.retry_count = 3;
rdma_create_event_channel(&ctx->ec);
rdma_create_id(ctx->ec, &ctx->id, ctx, RDMA_PS_TCP);
rdma_bind_addr(ctx->id, NULL);
rdma_listen(ctx->id, 10);
rdma_get_cm_event(ctx->ec, &event);
rdma_ack_cm_event(event);
rdma_get_cm_event(ctx->ec, &event);
if (event->event != RDMA_CM_EVENT_CONNECT_REQUEST) {
fprintf(stderr, "unexpected event: %s\n", rdma_event_str(event->event));
exit(EXIT_FAILURE);
}
id = event->id;
rdma_ack_cm_event(event);
memcpy(&ctx->remote_buffer_size, id->context->recv_buf, sizeof(ctx->remote_buffer_size));
register_memory(ctx);
conn_param.private_data = &ctx->remote_buffer_size;
conn_param.private_data_len = sizeof(ctx->remote_buffer_size);
rdma_accept(id, &conn_param);
rdma_get_cm_event(ctx->ec, &event);
if (event->event != RDMA_CM_EVENT_ESTABLISHED) {
fprintf(stderr, "unexpected event: %s\n", rdma_event_str(event->event));
exit(EXIT_FAILURE);
}
rdma_ack_cm_event(event);
}
int main(int argc, char **argv)
{
struct rdma_context ctx;
build_context(&ctx);
build_connection(&ctx);
on_connect(ctx.id);
while (1) {
struct ibv_cq *cq;
struct ibv_wc wc;
int ne;
ne = ibv_poll_cq(ctx.cq, 1, &wc);
if (ne < 0) {
fprintf(stderr, "ibv_poll_cq failed\n");
exit(EXIT_FAILURE);
} else if (ne > 0) {
if (wc.status != IBV_WC_SUCCESS) {
fprintf(stderr, "completion with error: %s\n", ibv_wc_status_str(wc.status));
} else {
on_completion(&wc);
}
} else {
/* no work queued, wait for next event */
}
}
destroy_context(&ctx);
return 0;
}
RDMA技术是一种具有优异性能的网络传输协议,能够提高数据传输的吞吐量、降低延迟和CPU负载。在高性能计算、HPC、云计算等领域得到广泛应用。RDMA编程需要了解RDMA硬件和驱动细节、RDMA API的使用方式和Verbs API的使用方式。在Linux内核中,从2.6.27版本开始支持RDMA技术。RDMA应用实例包括数据库、存储、分布式计算、云计算和视频传输等场景。