在前文中(参考上面的推荐阅读),我们提到为了便于用户使用用户态的block device, SPDK中开发了用户态、无锁、轮询的block device (bdev) layer。
Block device layer 主要由SPDK中spdk/lib/bdev目录中的代码实现,而为了实现一些必要的块设备及其相应的功能,spdk/module/bdev/中已经实现的一些常见的bdev module。
$ ll lib/bdev
bdev.c bdev_internal.h bdev_rpc.c bdev_zone.c
Makefile part.c scsi_nvme.c vtune.c
$ ll module/bdev/
aio/ compress/ crypto/ delay/ error/ ftl/ gpt/
iscsi/ lvol/ Makefile malloc/ null/ nvme/ ocf/
passthru/ pmem/ raid/ rbd/ rpc/ split/ uring/
virtio/ zone_block/
SPDK的block device layer为用户提供了很多实用的功能,同时也抽象出了一套适合所有块设备的设计:
-
在内存已经用完或者(设备的I/O)队列已满的情况下,会自动将新的I/O请求组成队列。
-
即使设备正在处理I/O, 也可以热插拔。
-
统计块设备的I/O数据比如带宽和延迟。
-
支持设备重启和I/O超时追踪。
除了这些特点之外,本文将详细剖析SPDK bdev layer:讲解bdev layer作为SPDK application中的一个subsystem,SPDK是如何初始化它的。在bdev这个subsystem中,每一个bdev module 以及其中的bdev又是如何初始化的。
注意,本文中使用了bdev layer是表示整个bdev 抽象层,而 bdev subsystem是SPDK中确切存在的一个subsystem。另外,本文所讲的初始化不涉及RPC。
SPDK bdev layer的初始化:
从subsystem的初始化开始
通常我们使用SPDK bdev 都是在SPDK application的框架下。SPDK application开始运行后,程序在进入main函数之前会首先注册application会用到的几个SPDK现有的subsystem: copy, bdev, iscsi, nbd,interface, net framework, nvmf, scsi, vhost, vmd。这些subsystem的注册是通过C语言的函数属性(主要是” attribute __constructor__”)来实现的。
在完成注册之后,SPDK application首先会解析输入的参数。然后在当前线程上创建一个spdk_thread:在SPDK中,只有创建了spdk_thread之后才能初始化bdev subsystem。此后注册的subsystems将会被依次初始化,本文仅仅关注bdev subsystem的初始化,而