ZBLOG

spdk用reactor实现网络io处理的代码案例

SPDK(Storage Performance Development Kit)是一个专为高性能存储应用程序而设计的开源工具包,提供了一组库和驱动程序,帮助用户构建高性能、低延迟的存储应用程序。下面是一个使用 Reactor 实现网络 I/O 处理的 SPDK 代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>

#include "spdk/stdinc.h"
#include "spdk/nvme.h"
#include "spdk/env.h"
#include "spdk/event.h"

static void
nvme_connect_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
                struct spdk_nvme_ctrlr_opts *opts)
{
    opts->io_queue_size = 1024;
}

static bool g_exit = false;

static void
sig_handler(int sig)
{
    if (sig == SIGINT || sig == SIGTERM) {
        g_exit = true;
    }
}

struct reactor_io_channel {
    struct spdk_poller *poller;
};

static int
reactor_io_channel_create(void *io_device, void *ctx_buf)
{
    struct reactor_io_channel *ch = ctx_buf;

    ch->poller = NULL;
    return 0;
}

static int
reactor_io_channel_destroy(void *io_device, void *ctx_buf)
{
    return 0;
}

static uint32_t
reactor_io_channel_get_max_events(void *ctx_buf)
{
    return INT_MAX;
}

static int
reactor_add_event(struct reactor_io_channel *ch, struct spdk_event *event)
{
    if (!ch->poller) {
        ch->poller = spdk_poller_register(
            [](void *arg) {
                struct reactor_io_channel *ch = (struct reactor_io_channel *)arg;
                spdk_event *evt;

                while ((evt = spdk_event_get())) {
                    spdk_event_call(evt);
                }
            },
            ch, 0);
    }

    spdk_event_call(event);

    return 0;
}

static void
reactor_remove_event(struct reactor_io_channel *ch, struct spdk_event *event)
{
}

static struct spdk_io_channel *
reactor_create_ch(void)
{
    struct reactor_io_channel *ch = calloc(1, sizeof(*ch));
    if (!ch) {
        return NULL;
    }

    return spdk_allocate_io_channel(ch, reactor_io_channel_create,
                                     reactor_io_channel_destroy,
                                     reactor_io_channel_get_max_events);
}

static void
reactor_submit_request(struct spdk_nvme_qpair *qpair, struct nvme_request *req)
{
    req->cb_arg = req;

    int rc = nvme_qpair_submit_request(qpair, &req->nvme_req);
    if (rc != 0) {
        printf("submit request failed: %d\n", rc);
        abort();
    }
}

int main(int argc, char **argv)
{
    struct spdk_env_opts opts;
    int rc;

    memset(&opts, 0, sizeof(opts));
    opts.name = "spdk_reactor";
    opts.core_mask = "0x1";
    
     //初始化环境
     if ((rc = spdk_env_init(&opts)) < 0) {
         fprintf(stderr,"Unable to initialize SPDK env: %d\n", rc);
         return -1;
     }

     // 设置回调函数,当连接建立时调用
     if ((rc = spdk_nvme_probe(NULL /*全部控制器*/, NULL, nvme_connect_cb, NULL, NULL)) < 0) {
         fprintf(stderr,"nvme_probe() failed: %d\n", rc);
         return -1;
     }

     // 创建 I/O 线程
     struct spdk_io_channel *ch = reactor_create_ch();
     if (!ch) {
         printf("create io channel failed\n");
         return -1;
     }

    // 获取 I/O 线程的通道,该通道会被用于提交请求和处理回调函数
    struct reactor_io_channel *reactor_ch = spdk_io_channel_get_ctx(ch);

    // 打开 NVMe 设备
    struct spdk_nvme_ctrlr *ctrlr = NULL;

    const char *name = "NVME01";
    const char *pci_addr = "0000:81:00.0";
    rc = spdk_nvme_probe(NULL, (void *)name, nvme_connect_cb, NULL,
                         &ctrlr);
    if (rc != 1) {
        fprintf(stderr,"failed to probe for device '%s': %d\n", name, rc);
        return -1;
    }

    printf("Open device '%s' success\n", name);

   // 创建队列对(IO 操作将在此进行)
   int qid = 0;
   struct spdk_nvme_qpair *qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts,
                                                                   sizeof(opts), ch, qid);
   if (!qpair) {
       printf("allocate queue pair failed\n");
       return -1;
   }

   // 分配请求内存池并从中获取请求对象
   struct nvme_request_pool req_pool;
   req_pool.pool_size = 1024;
   req_pool.req_size = sizeof(struct nvme_request);
   req_pool.align = 0;
   if (nvme_request_pool_init(&req_pool)) {
       printf("request pool init failed\n");
       return -1;
   }

   struct nvme_request *req = NULL;

   // 分配一个请求对象
    req = (struct nvme_request *)nvme_request_pool_get(&req_pool, 0);
    if (!req) {
        printf("get request from pool failed\n");
        return -1;
    }

    memset(&req->nvme_req, 0, sizeof(req->nvme_req));
    req->cb_fn = [](void *cb_arg, const struct spdk_nvme_cpl *cpl) {
        printf("I/O complete: %d/%d\n", cpl->status.sc, cpl->status.sct);

        g_exit = true;
    };

    // 发送读取命令
    rc = spdk_nvme_ctrlr_cmd_read(ctrlr, qpair, "hello world!", 0x100 /*读取的长度*/, 0 /*起始地址*/,
                                  req->buf,
                                  NULL /*元数据指针*/,
                                  0,
                                  req->cb_fn,
                                  &g_exit /*回调函数的参数*/);
    
     if (rc != 0) {
         printf("submit read command failed: %d\n", rc);
         return -1;
     }

     while (!g_exit) {
         reactor_add_event(reactor_ch, spdk_event_allocate(reactor_ch,
                                                            [](void *, void *) {}, NULL));
     }

     // 清理资源
     nvme_qpair_free(qpair);
     spdk_nvme_detach(ctrlr);
     spdk_env_cleanup();

     return 0;
}

在这个示例中,我们首先初始化 SPDK 环境,然后使用 spdk_nvme_probe() 函数打开 NVMe 设备。接下来,我们创建 I/O 线程,并在其中创建了一个 I/O 通道。通过该通道,我们可以向 NVMe 队列对中提交 I/O 请求,并处理响应。

在示例中,我们定义了一个 reactor_io_channel 结构体,用于管理 Reactor 的事件循环。当有请求需要处理时,我们会将其作为事件添加到 Reactor 中,并在 Reactor 中处理这些事件。

最后,在主函数中,我们分配了一个请求对象,并使用 spdk_nvme_ctrlr_cmd_read() 函数向设备发出读取命令。当读取完成时,回调函数会被调用并设置退出标志变量 g_exit。主线程的循环等待 g_exit 变量变为 true 后退出程序并清理资源。

这个示例只是 SPDK 的一小部分功能演示,具体实现可能因场景而异。如果您想要深入学习 SPDK,请参考 SPDK 官方文档或者零声教育的课程内容。


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=1566

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?