学习spdk

简介: 学习spdk


学习SPDK,可以按照以下步骤进行:

  1. 了解SPDK的基本概念和原理,包括SPDK的优势、设计思路、应用场景等。
  2. 学习SPDK的编程模型和API,了解SPDK的核心组件和应用接口,熟悉SPDK的编程规范和使用方法。
  3. 进行SPDK的实践和应用,可以通过编写简单的SPDK应用程序来熟悉SPDK的使用,也可以参与SPDK社区的开源项目,了解SPDK的实现细节和技术难点。
  4. 参与SPDK社区的交流和讨论,可以通过SPDK的邮件列表、论坛、社交媒体等渠道,与SPDK的开发者和用户进行交流和分享,了解SPDK的最新发展和应用案例。

总之,学习SPDK需要有一定的编程经验和相关知识,需要不断地实践和探索,也需要积极参与社区的开发和交流。

SPDK的优势

  • 零拷贝:SPDK可以避免数据从用户空间到内核空间的复制,直接在用户空间操作I/O数据,提高I/O操作效率。
  • 高性能:SPDK使用异步I/O操作和多线程技术,充分利用多核CPU的性能,提高I/O操作的吞吐量和响应速度。
  • 可扩展性:SPDK支持多种硬件加速技术,如RDMA、NVMe等,可以在不同的硬件平台上实现高性能的存储服务。
  • 开源:SPDK是一个开源项目,可以自由获取、使用和修改,有广泛的社区支持和应用案例。

SPDK的设计思路

  • 用户空间I/O:SPDK将I/O操作从内核空间转移到用户空间,避免了复杂的内核调度和同步机制,提高了I/O操作的效率。
  • 异步I/O:SPDK使用异步I/O操作,可以在I/O操作等待时执行其他任务,提高了系统的并发性和响应速度。
  • 多线程:SPDK采用多线程技术,充分利用多核CPU的性能,提高I/O操作的吞吐量和响应速度。
  • 零拷贝:SPDK可以避免数据从用户空间到内核空间的复制,直接在用户空间操作I/O数据,提高I/O操作效率。

SPDK的应用场景

  • 存储系统:SPDK可以支持高性能的存储系统,如块存储、文件存储、对象存储等。
  • 数据库系统:SPDK可以支持高性能的数据库系统,如NoSQL数据库、分布式数据库等。
  • 数据分析系统:SPDK可以支持高性能的数据分析系统,如Hadoop、Spark等。
  • 云计算平台:SPDK可以支持高性能的云计算平台,如OpenStack、Kubernetes等。

SPDK的编程模型和API

SPDK的编程模型基于异步I/O和事件驱动,开发者需要使用SPDK提供的异步I/O API来执行I/O操作,使用事件驱动机制来处理I/O完成事件。SPDK提供了一系列异步I/O API,包括I/O通道、I/O缓冲区、I/O队列、I/O完成事件等。开发者需要使用这些API来构建自己的存储服务或应用程序。

SPDK的核心组件和应用接口

SPDK的核心组件包括了多个模块,包括块设备模块、NVMe设备模块、事件模块、异步I/O模块、内存模块、日志模块等。SPDK还提供了多个应用接口,包括初始化接口、块设备接口、NVMe设备接口、I/O通道接口、I/O队列接口、异步I/O接口等。开发者可以使用这些接口来构建自己的存储服务或应用程序。

SPDK的编程规范和使用方法

为了保证代码质量和可维护性,SPDK社区制定了一系列编程规范和使用方法,包括代码风格、注释规范、函数命名规范、错误处理规范、内存管理规范等。开发者需要遵循这些规范和方法来进行开发工作,以便其他开发者或社区成员能够更好地理解和维护代码。

此外,为了让开发者更容易上手SPDK的开发工作,SPDK还提供了完整的文档和教程,包括API文档、用户指南、示例代码、视频教程等。开发者可以根据自己的需求选择合适的文档和教程来进行学习和实践。

以下是一些SPDK相关的详细链接,供参考:

SPDK官网:https://spdk.io/

SPDK GitHub仓库:https://github.com/spdk/spdk

SPDK文档中心:https://spdk.io/doc/

SPDK社区邮件列表:https://lists.01.org/mailman/listinfo/spdk

SPDK社区论坛:https://forum.spdk.io/

SPDK社区Slack频道:https://spdk.slack.com/archives/C029VC7M8GP

SPDK社区YouTube频道:https://www.youtube.com/channel/UCbX9lJfY6b2cKQyj6hBgySw

SPDK社区Twitter账号:https://twitter.com/SPDK_IO

Intel官方SPDK介绍视频:https://www.youtube.com/watch?v=L2_6NnYrKbY&t=11s

SPDK学习视频教程(中文):https://www.bilibili.com/video/BV1p54y1v7zT

SPDK最佳实践视频教程(英文):https://www.youtube.com/watch?v=Zv3qP-qo5A0&t=1s

SPDK NVMe-oF视频教程(英文):https://www.youtube.com/watch?v=7VU4pNl8GvU

SPDK在云计算中的应用视频教程(英文):https://www.youtube.com/watch?v=3xLZzCJH9yk&t=1s

spdk关于nvme模块的helloword实例代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <rte_config.h>
#include <rte_eal.h>
#include <rte_pci.h>
#include <rte_bus_pci.h>
#include <rte_memory.h>
#include <spdk/nvme.h>
static bool g_shutdown = false;
static void sig_handler(int signum)
{
    if (signum == SIGINT || signum == SIGTERM) {
        g_shutdown = true;
    }
}
int main(int argc, char **argv)
{
    struct spdk_nvme_ctrlr *ctrlr;
    struct spdk_nvme_ns *ns;
    struct spdk_nvme_qpair *qpair;
    struct spdk_nvme_ctrlr_opts opts;
    struct spdk_pci_addr pci_addr;
    uint32_t nsid;
    int rc;
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
    rc = rte_eal_init(argc, argv);
    if (rc < 0) {
        fprintf(stderr, "Failed to initialize EAL\n");
        return -1;
    }
    memset(&opts, 0, sizeof(opts));
    opts.num_io_queues = 4;
    ctrlr = spdk_nvme_connect(NULL, &opts, sizeof(opts));
    if (ctrlr == NULL) {
        fprintf(stderr, "Failed to connect to NVMe controller\n");
        return -1;
    }
    pci_addr = spdk_nvme_ctrlr_get_pci_controller(ctrlr)->addr;
    printf("Connected to NVMe controller at %!!(MISSING)x(MISSING):%!!(MISSING)x(MISSING):%!!(MISSING)x(MISSING).%!!(MISSING)x(MISSING)\n",
           pci_addr.domain, pci_addr.bus, pci_addr.devid, pci_addr.function);
    nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr);
    if (nsid == 0xffffffff) {
        fprintf(stderr, "No active namespace found\n");
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
    if (ns == NULL) {
        fprintf(stderr, "Failed to get namespace %!!(MISSING)u(MISSING)\n", nsid);
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    printf("Connected to NVMe namespace %!!(MISSING)u(MISSING)\n", nsid);
    qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
    if (qpair == NULL) {
        fprintf(stderr, "Failed to allocate I/O queue pair\n");
        spdk_nvme_ns_detach(ns);
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    while (!g_shutdown) {
        printf("Hello, World!\n");
        sleep(1);
    }
    spdk_nvme_ctrlr_free_io_qpair(qpair);
    spdk_nvme_ns_detach(ns);
    spdk_nvme_detach(ctrlr);
    return 0;
}

此示例程序将连接到系统中的第一个NVMe控制器,打开其第一个活动命名空间,并分配一个I/O队列对。然后在循环中打印“Hello, World!”,直到接收到SIGINT或SIGTERM信号时退出程序。

详细讲解

首先,在main函数中,我们进行了一些初始化操作。首先,使用rte_eal_init函数对EAL(Environment Abstraction Layer)进行初始化,这是DPDK(Data Plane Development Kit)的一部分,用于抽象出底层平台的特定性,使得DPDK可以在不同的平台上运行。初始化后,我们使用memset函数将opts(struct spdk_nvme_ctrlr_opts结构体类型)的所有成员都初始化为0。然后,我们使用spdk_nvme_connect函数连接到系统中的第一个NVMe控制器,这个函数的第一个参数为NULL,表示使用默认的选项进行连接;第二个参数为opts,表示我们使用之前初始化的opts结构体作为选项;第三个参数是opts的大小。如果连接失败,我们会输出错误信息并直接退出程序。

struct spdk_nvme_ctrlr *ctrlr;
    struct spdk_nvme_ns *ns;
    struct spdk_nvme_qpair *qpair;
    struct spdk_nvme_ctrlr_opts opts;
    struct spdk_pci_addr pci_addr;
    uint32_t nsid;
    int rc;
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
    rc = rte_eal_init(argc, argv);
    if (rc < 0) {
        fprintf(stderr, "Failed to initialize EAL\n");
        return -1;
    }
    memset(&opts, 0, sizeof(opts));
    opts.num_io_queues = 4;
    ctrlr = spdk_nvme_connect(NULL, &opts, sizeof(opts));
    if (ctrlr == NULL) {
        fprintf(stderr, "Failed to connect to NVMe controller\n");
        return -1;
    }

接下来,我们获取连接上的NVMe控制器的PCI地址,并将其打印出来。

pci_addr = spdk_nvme_ctrlr_get_pci_controller(ctrlr)->addr;
    printf("Connected to NVMe controller at %!!(MISSING)!(MISSING)x(MISSING):%!!(MISSING)!(MISSING)x(MISSING):%!!(MISSING)!(MISSING)x(MISSING).%!!(MISSING)!(MISSING)x(MISSING)\n",
           pci_addr.domain, pci_addr.bus, pci_addr.devid, pci_addr.function);

然后,我们打开第一个活动的命名空间(namespace)。

nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr);
    if (nsid == 0xffffffff) {
        fprintf(stderr, "No active namespace found\n");
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
    if (ns == NULL) {
        fprintf(stderr, "Failed to get namespace %!!(MISSING)!(MISSING)u(MISSING)\n", nsid);
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    printf("Connected to NVMe namespace %!!(MISSING)!(MISSING)u(MISSING)\n", nsid);

最后,我们分配一个I/O队列对,并在循环中打印“Hello, World!”,直到接收到SIGINT或SIGTERM信号时退出程序。需要注意的是,这里我们使用了spdk_nvme_ctrlr_alloc_io_qpair函数来分配I/O队列对,而不是使用spdk_nvme_ctrlr_get_default_io_qpair函数,是因为前者可以为I/O队列对设置一些选项,而后者则不能。

qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
    if (qpair == NULL) {
        fprintf(stderr, "Failed to allocate I/O queue pair\n");
        spdk_nvme_ns_detach(ns);
        spdk_nvme_detach(ctrlr);
        return -1;
    }
    while (!g_shutdown) {
        printf("Hello, World!\n");
        sleep(1);
    }
    spdk_nvme_ctrlr_free_io_qpair(qpair);
    spdk_nvme_ns_detach(ns);
    spdk_nvme_detach(ctrlr);

当程序退出时,我们需要使用spdk_nvme_ctrlr_free_io_qpair函数释放I/O队列对,然后分别使用spdk_nvme_ns_detach和spdk_nvme_detach函数分离命名空间和控制器。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关文章
|
6月前
|
存储 缓存 算法
【自己动手画CPU】存储系统设计
博文“【自己动手画CPU】存储系统设计”探讨了在自制 CPU 中存储系统的设计。存储系统是计算机中至关重要的组成部分,负责存储和检索数据。文章介绍了在 DIY CPU 中实现存储系统的关键考虑因素,包括存储器的类型、存储器与 CPU 的连接方式以及数据存取的速度和效率。通过深入探讨存储系统的设计原理和实现方式,读者可以更好地理解计算机内部结构,并且为自己动手设计和构建 CPU 提供了有益的指导和启发。
177 0
【自己动手画CPU】存储系统设计
|
存储 缓存 安全
高并发内存池实战:用C++构建高性能服务器(下)
高并发内存池实战:用C++构建高性能服务器
高并发内存池实战:用C++构建高性能服务器(下)
|
Linux Anolis 异构计算
关于远程直接内存访问技术 RDMA 的高性能架构设计介绍
本文介绍 RDMA 技术的基本原理及交流在工程上的设计思路。
|
2月前
|
存储 安全 算法
探索操作系统的心脏:内核架构与机制的深度剖析
本文旨在深入探讨操作系统的核心——内核,揭示其架构设计与运行机制的内在奥秘。通过对进程管理、内存管理、文件系统、设备控制及网络通信等关键组件的细致分析,展现内核如何高效协调计算机硬件与软件资源,确保系统稳定运行与性能优化。文章融合技术深度与通俗易懂的表述方式,旨在为读者构建一幅清晰、立体的内核运作全景图。
69 0
|
4月前
|
存储 缓存 运维
Lustre架构介绍的阅读笔记-HSM
HSM(Hierarchical Storage Management)是数据分级存储管理,根据数据生命周期、访问特性和设备成本,自动在CPU寄存器、缓存、主存、SSD、HDD、光盘、磁带库等不同存储层级间迁移数据。数据热度分为热、温、冷、冰,对应不同成本、性能和容量。迁移策略可基于人工判断或系统自动计算,并确保业务I/O不受影响、数据一致性。访问频率增加时,数据可反向迁移至更高层级。
|
存储 缓存 安全
从原理到实践:掌握DPDK内存池技术(下)
从原理到实践:掌握DPDK内存池技术
|
存储 缓存 Unix
从原理到实践:掌握DPDK内存池技术(上)
从原理到实践:掌握DPDK内存池技术
|
6月前
|
固态存储 API 内存技术
多看看spdk代码学习
多看看spdk代码学习
|
6月前
|
存储 缓存 固态存储
SPDK应用框架
SPDK应用框架
|
存储 缓存 Linux
高并发内存池实战:用C++构建高性能服务器(上)
高并发内存池实战:用C++构建高性能服务器