16. SPDK应用框架

仅仅提供用户态NVMe驱动的一些操作函数是不够的,如果在某些应用场景中使用不当,不仅不能发挥出用户态NVMe驱动的高性能的作用,甚至会导致程序出现错误。
虽然NVMe的底层函数有一些说明,但为了更好地发挥出底层NVMe的性能,SPDK提供了一套编程框架(Application Framework)如下图所示,用于指导软件开发人员基于SPDK的用户态NVMe驱动及用户态块设备层构造高效的存储应用。用户可以有以下两种选择。

  • 直接使用SPDK应用编程框架实现应用的逻辑。
  • 利用SPDK编程框架的思想,改造已有应用的编程逻辑,以更好地适配SPDK的用户态NVMe驱动。
SPDK编程框架


总的来说,SPDK的应用框架可以分为:

  • 对CPU core和线程的管理
  • 线程间的高效通信
  • I/O的处理模型
  • 数据路径的无锁化机制

对CPU core和线程的管理

SPDK的原则是使用最少的CPU核和线程来完成最多的任务。为此SPDK在初始化程序的时候限定使用绑定CPU的哪些核。可以在配置文件或命名行中配置,如在命令行中使用“-c 0x5”,这是指使用core 0和core 2来启动程序。(其中0x5对应的二进制表示为 00000101,从低位到高位分别代表对应的核的编号)

通过CPU核绑定函数的亲和性,可以限制对CPU的使用,并且在每个核上运行一个thread,这个thread在SPDK中叫作Reactor。目前SPDK的环境库默认使用了DPDK的EAL库来进行管理。

总的来说,这个Reactorthread 执行一个函数_spdk_reactor_run , 这个函数的主体包含一个“while(1){}”,直到这个Reactor的state被改变。

为了提高效率,这个循环中也会有一些相应的机制让出CPU资源,如sleep,这样的机制在很多时候会导致CPU使用100%的情况,类似于DPDK。

也就是说,一个使用SPDK编程框架的应用,假设使用了两个CPUcore,每个core上就会启动一个Reactor thread,那么用户怎么执行自己的函数呢?

为了解决这个问题,SPDK提供了一个Poller机制。所谓Poller,其实就是用户定义函数的封装。SPDK提供的Poller分为两种:基于定时器的Poller和基于非定时器的Poller。SPDK的Reactor thread对应的数据结构由相应的列表来维护Poller的机制,比如一个链表维护定时器的Poller,另一个链表维护非定时器的Poller,并且提供Poller的注册及销毁函数

在Reactor的while循环中,会不停地检查这些Poller的状态,并且进行相应的调用,这样用户的函数就可以进行相应的执行了。由于单个CPU核上,只有一个Reactor thread,所以同一个Reactor thread中不需要一些锁的机制来保护资源。当然位于不同CPU核上的thread还是有通信的必要的。为此,SPDK封装了线程间异步传递消息(Async Messaging Passing)的功能

线程间的高效通信

SPDK放弃使用传统的、低效的加锁方式来进行线程间的通信。为了使同一个thread 只执行自己所管理的资源, SPDK 提供了事件调用(Event)的机制。这个机制的本质是每个Reactor对应的数据结构维护了一个Event事件的环,这个环是多生产者和单消费者(Multiple Producer Single Consumer,MPSC)的模型,意思是每个Reactor thread可以接收来自任何其他Reactor thread(包括当前的Reactor thread)的事件消息进行处理
目前SPDK中这个Event环的默认实现依赖于DPDK的机制,这个环应该有线性的锁的机制,但是相比较于线程间采用锁的机制进行同步,要高效得多。毫无疑问的是, 这个Event 环其实也在Reactor 的函数_spdk_reactor_run中进行处理。每个Event事件的数据结构包括了需要执行
的函数和相应的参数,以及要执行的core。

简单来说,一个Reactor A向另外一个Reactor B通信,其实就是需要 Reactor B执行函数F(X),X是相应的参数。基于这样的机制,SPDK就 实现了一套比较高效的线程间通信的机制,具体例子可以参照SPDK NVMe-oF Target内部的一些实现,主要代码位于lib/nvmf目录下。

I/O的处理模型及数据路径的无锁化机制

SPDK主要的I/O处理模型是运行直到完成。如前所述,使用SPDK应用框架, 一个CPU core 只拥有一个thread , 这个thread 可以执行很多Poller(包括定时器和非定时器)。运行直到完成的原则是让一个线程最好执行完所有的任务。
显而易见,SPDK的编程框架满足了这个需要。如果不使用SPDK应用编程框架,则需要编程者自己注意这个事项。比如使用SPDK用户态NVMe驱动访问相应的I/O QPair进行读/写操作,SPDK提供了异步读/写的函数spdk_nvme_ns_cmd_read , 以及检查是否完成的函数spdk_nvme_qpair_process_completions,这些函数的调用应当由一个线程去完成,而不应该跨线程去处理。

<think>我们在用户的上一次问题中了解到用户想查找在Linux系统下使用NVMe和SPDK应用例程。根据引用[3]提到,SPDK的示例代码位于examples目录中,并且示例在构建过程中自动编译。用户可以通过运行这些示例来了解SPDK的使用。特别提到了`examples/nvme/identify/identify`程序,它可以打印系统上所有NVMe设备的信息。此外,在app目录下还有更大的应用程序,如iSCSI和NVMe-oF target。 因此,我们可以指导用户如何获取和运行SPDK的示例程序。 步骤: 1. 获取SPDK源代码 2. 构建SPDK(包括示例程序) 3. 运行示例程序(如identify) 另外,引用[1]提到SPDK提供了一套编程框架,用户可以直接使用该框架实现应用逻辑,或者改造现有应用逻辑以适配SPDK。因此,我们也可以提供一些基于SPDK框架开发应用的指导。 下面我们给出具体步骤和示例。 </think>### Linux系统下NVMe SPDK应用例程解析 基于提供的引用信息,以下是SPDK在Linux系统中的典型应用例程及操作方法: --- #### **1. 基础环境准备** - **系统要求**:Linux内核(建议5.4+),启用IOMMU(否则需root权限运行)[^3]。 - **获取SPDK源码**: ```bash git clone https://github.com/spdk/spdk cd spdk && git submodule update --init ``` - **编译与依赖项安装**: ```bash sudo scripts/pkgdep.sh # 安装依赖 ./configure # 配置环境 make -j$(nproc) # 编译SPDK及所有示例程序 ``` --- #### **2. 核心应用例程** ##### (1) **NVMe设备识别与信息获取** - 运行设备识别工具(需NVMe设备已连接): ```bash sudo ./build/examples/nvme/identify/identify # 打印所有NVMe设备详细信息 ``` **输出示例**: ```plaintext ======== NVMe Controller at 0000:01:00.0 ======== Model Number: Samsung SSD 970 PRO Serial Number: S467NE0M123456 Firmware Revision: 1B2QEXP7 ... ``` *用途*:验证设备连接状态,读取硬件参数[^3][^5]。 ##### (2) **用户态NVMe性能测试** - 运行`perf`工具测试I/O吞吐量: ```bash sudo ./build/examples/nvme/perf/perf -q 64 -s 4096 -t 10 -w randread # 64队列深度,4KB随机读 ``` **输出指标**:IOPS、延迟、带宽[^5]。 ##### (3) **NVMe-oF Target部署** - 启动NVMe over Fabrics目标服务(引用[4]): ```bash sudo ./scripts/rpc.py nvmf_create_transport -t TCP # 创建TCP传输层 sudo ./scripts/rpc.py bdev_nvme_attach_controller -b NVMe1 -t PCIe -a 0000:01:00.0 # 绑定NVMe设备 sudo ./scripts/rpc.py nvmf_create_subsystem nqn.2023-06.org.spdk:cnode1 -a # 创建子系统 sudo ./scripts/rpc.py nvmf_subsystem_add_listener nqn.2023-06.org.spdk:cnode1 -t TCP -a 192.168.1.100 -s 4420 # 监听IP端口 ``` *功能*:通过网络暴露本地NVMe设备,支持远程主机访问[^4]。 --- #### **3. 高级应用开发框架** SPDK提供两种开发模式(引用[1]): 1. **直接使用SPDK框架**: - 调用`libspdk`库实现I/O路径,避免内核上下文切换。 - 示例:`app/iscsi_tgt`(iSCSI存储服务)、`app/nvmf_tgt`(NVMe-oF服务)。 2. **改造现有应用逻辑**: - 通过`BDEV`块设备抽象层(类似Linux内核块设备层)接入SPDK[^2]。 - 示例:在Redis/Kafka中替换POSIX文件操作,使用`spdk_bdev_read/write`异步API。 --- #### **4. Vagrant快速开发环境**(引用[3]) - 通过预配置的Vagrant环境快速验证: ```bash cd spdk/contrib/vagrant vagrant up # 启动虚拟机 vagrant ssh # 登录后运行示例程序 ``` --- #### **关键注意事项** - **权限管理**:未启用IOMMU时需root运行,否则可配置用户权限[^3]。 - **性能优化**: - 使用`-m`参数绑定CPU核心(如`-m 0x1`绑定Core 0)。 - 启用轮询模式避免中断延迟(`-e 0xFFFF`禁用事件等待)。 - **错误处理**:通过`SPDK_LOG`环境变量输出调试日志(如`export SPDK_LOG=level:DEBUG`)。 > 提示:完整示例代码见SPDK源码的`examples/`和`app/`目录[^1][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值