还是干货直接上代码
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <signal.h> #include <stdbool.h> #include <pthread.h> #include <spdk/nvme.h> #define MAX_CONTROLLERS 16 static struct spdk_nvme_ctrlr *g_controllers[MAX_CONTROLLERS]; static int g_num_controllers = 0; static void ctrlr_init_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr_opts *opts) { printf("Initializing controller %!s(MISSING)\n", trid->traddr); } static void ctrlr_attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) { printf("Attached to controller %!s(MISSING)\n", trid->traddr); g_controllers[g_num_controllers++] = ctrlr; } static void ctrlr_detach_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr) { printf("Detached from controller %!s(MISSING)\n", spdk_nvme_ctrlr_get_trid(ctrlr)->traddr); } static void sigint_handler(int signal) { int i; printf("Caught signal %!d(MISSING)\n", signal); for (i = 0; i < g_num_controllers; i++) { spdk_nvme_detach(g_controllers[i]); } spdk_nvme_cleanup(); exit(0); } int main(int argc, char **argv) { struct spdk_env_opts opts; struct spdk_env_opts_init(&opts); opts.name = "helloworld"; opts.core_mask = "0x1"; opts.shm_id = 0; spdk_env_init(&opts); struct spdk_nvme_transport_id trid = { 0 }; const char *address = "0000:04:00.0"; trid.trtype = SPDK_NVME_TRANSPORT_PCIE; trid.adrfam = SPDK_NVMF_ADRFAM_IPV4; strncpy(trid.traddr, address, sizeof(trid.traddr)); struct spdk_nvme_ctrlr_opts ctrlr_opts; struct spdk_nvme_ctrlr_opts_set_defaults(&ctrlr_opts); ctrlr_opts.num_io_queues = 4; ctrlr_opts.use_cmb_sqs = true; struct spdk_nvme_probe_ctx *probe_ctx; probe_ctx = spdk_nvme_connect_async(&trid, &ctrlr_opts, sizeof(ctrlr_opts), ctrlr_init_cb, ctrlr_attach_cb, ctrlr_detach_cb); if (probe_ctx == NULL) { printf("Failed to connect to NVMe controller\n"); exit(1); } signal(SIGINT, sigint_handler); while (true) { spdk_nvme_probe_poll_async(probe_ctx); usleep(1000); } return 0; }
简易讲解
这个代码的作用是连接到一个NVMe SSD,并且打印出一些连接的信息。它的执行流程如下:
- 初始化SPDK环境。
- 定义NVMe控制器的传输ID和选项。
- 定义NVMe控制器连接的回调函数。
- 连接到NVMe控制器。
- 注册SIGINT信号处理函数。
- 循环调用spdk_nvme_probe_poll_async函数,等待NVMe控制器连接。
- 注销SPDK环境。
在这个代码中,我们使用了以下的NVMe相关函数:
- spdk_nvme_connect_async:异步连接到NVMe控制器。
- spdk_nvme_ctrlr_opts_set_defaults:设置NVMe控制器选项的默认值。
- spdk_nvme_probe_poll_async:异步探测NVMe控制器。
- spdk_nvme_detach:断开与NVMe SSD的连接。
- signal:注册信号处理函数。
这个代码演示了如何使用SPDK的NVMe模块连接到NVMe SSD,并且可以扩展它来执行更复杂的操作。在实际的应用程序中,我们可以根据需要修改这个代码,比如读取数据、使用多个队列、使用多个命名空间等。
详细讲解一下这份代码
首先,我们需要了解一下NVMe(Non-Volatile Memory Express)是什么
。NVMe是一个通用的高速输入/输出接口,用于连接Flash和其他非易失性存储器设备。它被设计为一种高效、低延迟的接口,可以满足需要高速数据传输的应用程序的需求。NVMe是一种标准化接口,它提供了一个统一的命令集和控制器接口,可以让软件开发人员更容易地访问和管理存储设备。
那么,这份代码的作用是什么呢?它的主要功能是连接到一个NVMe SSD,并且打印出一些连接的信息。具体的执行流程如下:
- 初始化SPDK环境。
这一步非常简单,只需要调用spdk_env_init函数即可。当然,我们也可以通过spdk_env_opts_init函数来设置环境选项。 - 定义NVMe控制器的传输ID和选项。
在这个代码中,我们定义了一个struct spdk_nvme_transport_id类型的变量trid,用于指定NVMe SSD的传输ID。同时,我们也定义了一个struct spdk_nvme_ctrlr_opts类型的变量ctrlr_opts,用于设置NVMe控制器的选项,比如队列数量、命名空间等。 - 定义NVMe控制器连接的回调函数。
在这个代码中,我们定义了三个回调函数,分别是ctrlr_init_cb、ctrlr_attach_cb和ctrlr_detach_cb。这些回调函数会在NVMe控制器连接的不同阶段被调用,我们可以在这些回调函数中实现一些自定义的逻辑。 - 连接到NVMe控制器。
通过调用spdk_nvme_connect_async函数,我们可以异步地连接到NVMe控制器。这个函数会返回一个指向spdk_nvme_probe_ctx类型的结构体的指针,我们可以在之后的异步操作中使用这个结构体。 - 注册SIGINT信号处理函数。
我们可以通过调用signal函数来注册信号处理函数,这里我们注册了SIGINT信号的处理函数。 - 循环调用spdk_nvme_probe_poll_async函数,等待NVMe控制器连接。
在这个代码中,我们使用了一个循环来等待NVMe控制器的连接,这个循环中调用了spdk_nvme_probe_poll_async函数。这个函数会异步地探测NVMe控制器,如果探测到了NVMe控制器,就会调用之前定义的回调函数。我们可以在这些回调函数中实现一些自定义的逻辑,比如读取数据、使用多个队列、使用多个命名空间等。 - 注销SPDK环境。
当程序退出时,我们需要调用spdk_nvme_cleanup函数来注销SPDK环境。
这份代码涉及到了一些SPDK NVMe模块的API,比如spdk_nvme_connect_async、spdk_nvme_probe_poll_async、spdk_nvme_detach等。如果你想深入了解这些API的使用方法,可以参考SPDK官方文档。
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习