SPDK可以正常响应操作系统的下电流程以及Ctrl+c命令,意味着它可以正确处理 信号.
由于SPDK内部有元数据需要刷到盘上保存,因此正常的处理信号非常重要,关系到下次启动后是否能够正常恢复元数据.
此处的元数据主要指的是Blobstore下盘保存的部分,包括Superblobk部分,以及用于下次恢复的几个位图.
SPDK可以通过spdk-kill-instance RPC命令正常的退出进程.
spdk-kill-instance
这个RPC的核心处理,其实就是对spdk自己的进程 发送一个指定的信号.
root@e610-c:/home/brandon/spdk-dev# ./scripts/rpc.py -s 1.1.1.4 -p 5000 spdk_kill_instance --help
usage: rpc.py [options] spdk_kill_instance [-h] sig_name
positional arguments:
sig_name signal will be sent to server.
options:
-h, --help show this help message and exit
支持的sig_name包括:
{"SIGINT", SIGINT},
{"SIGTERM", SIGTERM},
{"SIGQUIT", SIGQUIT},
{"SIGHUP", SIGHUP},
{"SIGKILL", SIGKILL},
{"SIGUSR1", SIGUSR1},
其中SIGKILL类似于shell命令 kill - 9 回直接导致进程退出而不会做任何处理.
常见的SIGTERM或者SIGQUIT,会触发spdk走正常的进程退出流程.
1,信号的注册
注册的位置在:

其中spdk_app_start是每个spdk应用必然要调用的接口,不管是spdk_tgt还是支持nvmf协议的nvmf_tgt.
spdk_app_start实际运行在appthread上,即0号线程上. 当给进程发送sig的时候,由0号线程捕捉到并进__shutdown_signal处理函数.
2,信号的处理

1,从spdk_subsystem_fini开始,处理完之后处理下一个子系统spdk_subsystem_fini_next.
2,每个子系统的都带回调函数进去,最后处理spdk_reactors_stop
3,子系统是个纯内存结构,根据加载的子系统进行反向的处理.
以bdev子系统为例:
1,首先找到子系统的fini接口是否注册,如果注册就调用.
subsystem_fini_next():
while (g_next_subsystem) {
if (g_next_subsystem->fini) {
g_next_subsystem->fini();
return;
}
g_next_subsystem = TAILQ_PREV(g_next_subsystem, spdk_subsystem_list, tailq);
}
注册子系统的ini,一般通过这个结构注册,以bdev为例:
bdev.c:
static struct spdk_subsystem g_spdk_subsystem_bdev = {
.name = "bdev",
.init = bdev_subsystem_initialize,
.fini = bdev_subsystem_finish,
.write_config_json = bdev_subsystem_config_json,
};
其注册的就是bdev_subsystem_finish函数,其他的子系统由iSCSI,NBD,accel,sock等等.
4,bdev的退出流程:

bdev分为2种,一种是必须以其他bdev作为basebdev的设备(如lvol,raid, ocf),一种是直接可以创建操作的bdev(nvme bdev,malloc bdev,null设备等 )
所有的bev都会注册 module_fini 接口,进行该bdev的unregister操作,如果内部有依赖,需要内部处理,比如LVS上如果有lvol,需要先将lvol的bdev unregister之后,再unregiser LVS,这个流程在LVS内部保证.
以lvs(lvolstor为例):由于lvolstore是必须依赖其他bdev的设备,所以他会注册:
.fini_start = vbdev_lvs_fini_start,
static struct spdk_bdev_module g_lvol_if = {
.name = "lvol",
.module_init = vbdev_lvs_init,
.fini_start = vbdev_lvs_fini_start,
.async_fini_start = true,
.examine_disk = vbdev_lvs_examine,
.get_ctx_size = vbdev_lvs_get_ctx_size,
};
5, lvs的退出流程

总的来说,就是将每个lvs的内存位图都刷到盘上,保证下次上电直接取位图就可以恢复,不用全盘扫描恢复.
lvs流程走完之后,会寻找下一个subsystem的处理.直到所有subsystem处理完.
欢迎电邮leipang09@gmail.com讨论