3. 内存注册与注销

任务目标

编写一个最小可运行的RDMA内存注册程序,调用ibv_reg_mr注册一块内存缓冲区,打印返回的lkey、rkey及访问权限,随后调用ibv_dereg_mr注销并验证返回值,以确认注册/注销流程正确。

环境依赖

  • Linux系统,已安装libibverbs-dev(或等价RDMA用户态库)
  • 具备RDMA网卡(或使用软RoCE环境,如rdma_rxe内核模块)
  • 编译命令:gcc -o mr_demo mr_demo.c -libverbs

关键完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <infiniband/verbs.h>

#define BUFFER_SIZE 4096
#define ACCESS_FLAGS (IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_WRITE)

int main() {
    struct ibv_device **dev_list;
    struct ibv_context *ctx = NULL;
    struct ibv_pd *pd = NULL;
    struct ibv_mr *mr = NULL;
    char *buf = NULL;
    int num_devices, ret = 0;

    // 1. 获取设备列表
    dev_list = ibv_get_device_list(&num_devices);
    if (!dev_list || num_devices < 1) {
        fprintf(stderr, "未找到RDMA设备\n");
        return 1;
    }

    // 2. 打开第一个可用设备
    ctx = ibv_open_device(dev_list[0]);
    if (!ctx) {
        fprintf(stderr, "打开设备失败\n");
        ibv_free_device_list(dev_list);
        return 1;
    }
    ibv_free_device_list(dev_list);

    // 3. 分配保护域
    pd = ibv_alloc_pd(ctx);
    if (!pd) {
        fprintf(stderr, "分配保护域失败\n");
        ibv_close_device(ctx);
        return 1;
    }

    // 4. 分配并填充内存缓冲区
    buf = malloc(BUFFER_SIZE);
    if (!buf) {
        fprintf(stderr, "内存分配失败\n");
        ibv_dealloc_pd(pd);
        ibv_close_device(ctx);
        return 1;
    }
    memset(buf, 0xAB, BUFFER_SIZE);

    // 5. 注册内存区域
    mr = ibv_reg_mr(pd, buf, BUFFER_SIZE, ACCESS_FLAGS);
    if (!mr) {
        fprintf(stderr, "内存注册失败\n");
        free(buf);
        ibv_dealloc_pd(pd);
        ibv_close_device(ctx);
        return 1;
    }

    // 6. 打印注册结果
    printf("内存注册成功:\n");
    printf("  缓冲区地址: %p\n", buf);
    printf("  缓冲区长度: %d 字节\n", BUFFER_SIZE);
    printf("  lkey: 0x%x\n", mr->lkey);
    printf("  rkey: 0x%x\n", mr->rkey);
    printf("  访问权限: 0x%x (LOCAL_WRITE=%s, REMOTE_READ=%s, REMOTE_WRITE=%s)\n",
           ACCESS_FLAGS,
           (ACCESS_FLAGS & IBV_ACCESS_LOCAL_WRITE) ? "是" : "否",
           (ACCESS_FLAGS & IBV_ACCESS_REMOTE_READ) ? "是" : "否",
           (ACCESS_FLAGS & IBV_ACCESS_REMOTE_WRITE) ? "是" : "否");

    // 7. 注销内存区域
    ret = ibv_dereg_mr(mr);
    if (ret != 0) {
        fprintf(stderr, "内存注销失败, 返回值: %d\n", ret);
        free(buf);
        ibv_dealloc_pd(pd);
        ibv_close_device(ctx);
        return 1;
    }
    printf("内存注销成功\n");

    // 8. 清理资源
    free(buf);
    ibv_dealloc_pd(pd);
    ibv_close_device(ctx);
    return 0;
}

运行与验证

  1. 编译:gcc -o mr_demo mr_demo.c -libverbs
  2. 运行:./mr_demo
  3. 预期输出(示例,实际值因设备而异):
内存注册成功:
  缓冲区地址: 0x55f3a2c1a260
  缓冲区长度: 4096 字节
  lkey: 0x3e1
  rkey: 0x3e1
  访问权限: 0x7 (LOCAL_WRITE=是, REMOTE_READ=是, REMOTE_WRITE=是)
内存注销成功

通过判定标准:

  • 程序退出码为0,且依次打印“内存注册成功”和“内存注销成功”;
  • lkey与rkey均为非零值(若为零表示注册异常);
  • 访问权限值等于0x7(即三个标志位全部置位),与代码中设置的ACCESS_FLAGS一致;
  • 注销后无错误输出,资源正常释放。

常见问题

  • 未找到RDMA设备:确认内核已加载rdma_rxe或硬件驱动,可用ibv_devices命令检查。
  • 注册失败:检查缓冲区指针是否有效、长度是否为正、保护域是否有效;若使用软RoCE,确认rdma_rxe模块已正确绑定网络接口。
  • lkey/rkey为0:说明注册未成功或设备不支持,检查ibv_reg_mr返回值是否为NULL。