

SPDK Underprinning
SPDK Practice
SPDK Architecture
SPDK Initiator Architecture
SPDK Application Layer
SPDK Reactor
SPDK THREAD
SPDK POLLER
SPDK Initiator NVMf Core
NVMf Initiator Subsystem
NVMf Initiator Controller
NVMf Initiator Namespace
NVMf NVMe QueuePair(QP)
NVMf NVMe I/O Channel
SPDK Transport Layer
SPDK Target Architecture
SPDK Target NVMf Core
SPDK Target BDEV Layer
SPDK BDEV
SPDK BDEV IO-CHANNEL
SPDK BDEV IO
SPDK BDEV OPS
SPDK Target Backend Layer
NVMe
MALLOC
RAID
IO-Uring
LVS
Environment Abstraction Layer(DPDK)
In-Depth Research on DPDK Mechanisms
DPDK
Blobstore/BlobFS
iSCSI Target
NBD(Network Block Device)
FUSE
DAOS
Mooncake With SPDK



在高性能存储需求日益增长的时期,几乎所有 I/O 都不得不穿过一层厚重的内核协议栈. 一次看似简单的读写,实际要经历系统调用陷入内核、权限与上下文的切换、文件系统的逻辑处理、Block Layer 的请求合并与重新调度,再构建 BIO、提交到驱动,最后通过中断艰难地返回用户态. 这条路径上,每一次陷入与返回都伴随着寄存器保存与恢复,每一层抽象都可能引入锁的争夺与缓存行的来回失效,软件栈的“通用性”被层层叠加成实际的延迟与抖动. 在当时,这些开销被视为必须付出的代价: 为了换取统一的接口、权限隔离和硬件无关性,开发者接受了这条沉重却“正确”的路径.

当 NVMe 与 NVMe-oF 真正进入大规模部署后,这种矛盾被瞬间撕开. 硬件已经能在微秒级完成一次 I/O,单盘轻松支撑数十万 IOPS,多队列设计更是让并行提交变得轻而易举. 然而软件路径却几乎没有跟上, 每一次请求依然要反复陷入内核、争夺锁、经历调度与中断返回. 原本被高延迟设备掩盖的软件开销,此时赤裸裸地暴露出来,常常占满端到端延迟的大半. CPU 的大量周期被消耗在中断处理与自旋等待上,硬件提供的队列深度难以被有效填满,尾延迟也变得反复无常、难以预测. 对那些真正在意确定性和微秒级响应的场景来说,这条曾经“足够好”的通用路径,已经从可接受的实现,变成了必须正视的瓶颈. 在矛盾日益尖锐的情况下,社区并非没有尝试, 开发者先后探索了几条过渡路径,试图在不彻底推倒既有模型的前提下缓解问题:
- 第一条路是继续在内核内部深挖: 以 io_uring 为代表的异步接口大幅减少了系统调用次数,多队列 Block Layer、per-CPU 数据结构以及更细粒度的无锁化改造,也确实降低了部分路径的开销. 然而这些优化始终运行在内核的调度与中断框架之内,抢占、锁、上下文切换的基本约束并没有消失,只是被尽量推迟或分摊. 它像是给旧房子不断加固和局部改造,能撑得更久,却改变不了结构本身的沉重.
- 第二条路是局部绕过内核: 借助 VFIO 将 NVMe 设备直接交给用户进程,再在用户态自行实现队列管理与门铃操作,确实能避开大部分内核路径. 但这类方案往往止步于”能跑”,缺少统一的抽象、成熟的内存管理与多设备协同能力,最终变成每个项目自己维护的一套私有驱动. 性能上去了,工程复杂度和可维护性却成了新的负担,像是拆了东墙的砖去补西墙,解决了眼前的延迟,却留下了长期的技术债
- 第三条路则更为务实,也更为无奈: 在应用层用批量提交、深度队列和异步流水线去”掩盖”延迟, 通过堆积足够多的 in-flight 请求,让硬件的并行能力尽量被吃满,从而在吞吐上获得可观数字. 但这种做法本质上是在用复杂度和资源换取平均值的好看,尾延迟依然起伏不定,CPU 消耗与同步逻辑也愈发臃肿. 它没有真正去掉路径中的固定开销,只是让开销不那么容易被直接看见.
这三条路都在特定场景下取得了成绩,却也共同暴露出一个事实: 只要核心 I/O 路径仍然受制于内核的通用模型,优化就只能是局部的、补偿性的. 就在这些过渡方案不断被提出、又不断暴露出新局限的同时,另一条线索也在悄然酝酿.

网络领域已经先行一步, DPDK 用事实证明: 当硬件具备多队列与低延迟能力时,把驱动和数据路径完全放到用户态、用轮询取代中断、用 per-core 的无共享设计消除锁,可以换来数量级的性能提升和远更高的确定性. 这套方法论在高速网络上站稳脚跟后,自然有人开始反问: 存储为什么不能走同一条路?

NVMe 的硬件特征与现代网卡高度相似: 多队列、门铃机制、completion queue、低延迟. 此前在内核里做的各种优化,以及借助 VFIO 进行的局部用户态尝试,都已经从侧面证明了”用户态操作 NVMe 队列”在技术上是可行的. 缺的不是可行性,而是一次系统性的重新设计: 不再把用户态驱动当作临时绕过手段,而是把它提升为完整的运行时: 统一的内存管理、线程模型、模块化后端、以及面向应用的抽象层.

与此同时,推动这一切真正落地的力量也逐渐清晰起来. 一端是硬件能力与业务需求的共同挤压: 延迟与确定性的要求被不断推高,内核通用路径里那些固定成本变得越来越难以忍受. 另一端则是跨领域经验的自然迁移. 网络侧已经用轮询模式证明,把控制权交还给用户态软件能够换来完全不同的效率与确定性,而 NVMe 在队列机制与低延迟特征上与现代网卡高度相似,存储没有理由止步于局部绕过. 于是有人开始把 DPDK 已经验证过的核心思想: 环境抽象、reactor 模型、无锁通信、大页与 DMA 内存管理系统地引入存储场景,并围绕 NVMe 队列、块设备抽象以及上层协议,重新设计整条 I/O 路径.

