以spdk的vhost为例,介绍spdk进程启动到执行工作的整个流程。代码目录spdk/app/存放着各种类型app的入口文件,vhost.c中的main代表vhost类型的进程初始化入口。spdk进程的大体初始化流程如下:
--->main
--->spdk_app_opts_init
--->spdk_app_parse_args
--->spdk_app_start
--->spdk_reactors_init
--->spdk_thread_send_msg(xx, bootstrap_fn, NULL);
--->spdk_reactors_start
--->reactor_run 通过while(1) 方式不停执行_reactor_run
--->_reactor_run
首先进程从main入口进入之后需要初始化参数以及入参进行解析,初始化参数在spdk_app_opts_init接口中,主要是内存地址,socket句柄,文件打印等级,rpc通信句柄地址等基本信息尽心填充。 spdk_app_parse_args接口中对入参进行解析,并且会覆盖掉一部分已经初始化的参数。
spdk_app_start为核心内容初始化,主要初始化内容分布在以下几个接口中:
1. spdk_reactors_init接口主要为轮训运行前的初始化,比如:调用dpdk接口spdk_mempool_create对内存池进行初始化;通过spdk_thread_lib_init_ext接口事件通知机制的线程池;通过reactor_construct接口创建轮训事件的ring环。
2. 通过发送消息调用到bootstrap_fn(异步执行),bootstrap_fn中会在接口subsystem_sort中对各个子系统进行的依赖关系进行排序,然后根据依赖关系进行初始化;初始化的具体调用在spdk_subsystem_init_next接口中,初始入参为0,首先从链表g_subsystems中取出第一个子系统,然后当第一个子系统初始化完之后,会再次递归调用到spdk_subsystem_init_next接口(以bdev模块为例,当bdev子系统完成初始化之后会调用到bdev_initialize_complete, 该接口中会调用到spdk_subsystem_init_next,继续对下一个子系统进行初始化),直到所有子系统都初始化完成才退出。
void
spdk_subsystem_init_next(int rc)
{
if (!g_next_subsystem) {
g_next_subsystem = TAILQ_FIRST(&g_subsystems); //第一次进接口是获取第一个子系统
} else {
g_next_subsystem = TAILQ_NEXT(g_next_subsystem, tailq);
}
if (!g_next_subsystem) { //子系统为NULL时说明链表遍历完,都已经初始化完成
g_subsystems_initialized = true;
g_subsystem_start_fn(0, g_subsystem_start_arg);
return;
}
if (g_next_subsystem->init) {
g_next_subsystem->init(); //回调到各个子系统的初始化函数中
} else {
spdk_subsystem_init_next(0);
}
}
// 以bdev子系统为例
static void
bdev_initialize_complete(void *cb_arg, int rc)
{
spdk_subsystem_init_next(rc); //bdev子系统初始化完成之后再调用到改接口中,递归调用
}
static void
bdev_subsystem_initialize(void)
{
spdk_bdev_initialize(bdev_initialize_complete, NULL); //将初始化完成的接口注册
}
bootstrap_fn中还对rpc子系统的进行了初始化。
3. spdk_reactors_start为轮训的最终调用终点,该接口中会为每个核抛出一个reactor_run接口,每个核的reactor_run接口中进行轮训,无异常的情况下,每次都轮训一遍_reactor_run接口。
_reactor_run接口每次又会轮训一遍该核上的线程。到此,spdk的初始化彻底完成。
_reactor_run(struct spdk_reactor *reactor)
{
if (spdk_unlikely(TAILQ_EMPTY(&reactor->threads))) { //当前任务为空,立即退出
now = spdk_get_ticks();
reactor->idle_tsc += now - reactor->tsc_last;
reactor->tsc_last = now;
return;
}
TAILQ_FOREACH_SAFE(lw_thread, &reactor->threads, link, tmp) { //thread中有任务,便开始工作
thread = spdk_thread_get_from_ctx(lw_thread);
rc = spdk_thread_poll(thread, 0, reactor->tsc_last);
now = spdk_thread_get_last_tsc(thread);
if (rc == 0) {
reactor->idle_tsc += now - reactor->tsc_last;
} else if (rc > 0) {
reactor->busy_tsc += now - reactor->tsc_last;
}
reactor->tsc_last = now;
reactor_post_process_lw_thread(reactor, lw_thread);
}
}