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助手提供,问题来源于学员提问