前回記事の続き:QEMUソースコードの完全解析その8 —— 全モジュールを初期化する
この記事の内容に関する参考文献:
「 Linux オペレーティング システムに関する興味深い話」 —— Liu Chao、Geek Time
「QEMU /KVM」ソースコード分析とアプリケーション - Li Qiang、機械産業プレス
どうもありがとうございます!
QEMU モジュールを定義すると、type_init が呼び出されます。kvm を例に挙げると、kvm モジュールは accel/kvm/kvm-all.c に実装されています。このファイルには、次の 1 行のコードがあります。
type_init(kvm_type_init);
type_init はマクロ定義であり、include/qemu/module.h 内のコードは次のとおりです。
#define type_init(function) module_init(function, MODULE_INIT_QOM)
したがって、type_init(kvm_type_init) は次のように展開されます。
module_init(kvm_type_init, MODULE_INIT_QOM)
module_init の定義は include/qemu/module.h にあり、コードは次のとおりです。
#ifdef BUILD_DSO
void DSO_STAMP_FUN(void);
/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can
* distinguish "version mismatch" from "not a QEMU module", when the stamp
* check fails during module loading */
void qemu_module_dummy(void);
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_dso_module_init(function, type); \
}
#else
/* This should not be used directly. Use block_init etc. instead. */
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_module_init(function, type); \
}
#endif
BUILD_DSO はシステムで定義されていないため、実際には #else 分岐、つまり次のコードを受け取ります。
/* This should not be used directly. Use block_init etc. instead. */
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_module_init(function, type); \
}
module_init(kvm_type_init, MODULE_INIT_QOM) を次のように置き換えます。
static void __attribute__((constructor)) do_qemu_init_kvm_type_init(void)
{
register_module_init(kvm_type_init, MODULE_INIT_QOM);
}
register_module_init は util/module.c にあり、コードは次のとおりです。
void register_module_init(void (*fn)(void), module_init_type type)
{
ModuleEntry *e;
ModuleTypeList *l;
e = g_malloc0(sizeof(*e));
e->init = fn;
e->type = type;
l = find_type(type);
QTAILQ_INSERT_TAIL(l, e, node);
}
前回分析した find_type 関数も参照してください。ここで、理解を助けるために、同じファイル (実際には register_module_init の上) にコードを再度貼り付けます。
static ModuleTypeList *find_type(module_init_type type)
{
init_lists();
return &init_type_list[type];
}
QTAIL_INSERT_TAIL は当然 include/qemu/queue.h で定義されており、コードは次のとおりです。
#define QTAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \
(head)->tqh_circ.tql_prev->tql_next = (elm); \
(head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \
} while (/*CONSTCOND*/0)
したがって、 register_module_init(kvm_type_init, MODULE_INIT_QOM) の場合、 register_module_init 関数は実際には次のようになります。
void register_module_init(void (*fn)(void), module_init_type type)
{
ModuleEntry *e;
ModuleTypeList *l;
e = g_malloc0(sizeof(*e));
e->init = fn; //实际上是:e->init = kvm_type_init;
e->type = type; //实际上是:e->type = MODULE_INIT_QOM;
l = find_type(type); //实际上是:l = find_type(MODULE_INIT_QOM);
QTAILQ_INSERT_TAIL(l, e, node);
}
type_init(kvm_type_init) の場合、最後に MODULE_INIT_QOM のタイプである register_module_init(kvm_type_init, MODULE_INIT_QOM) を呼び出します。MODULE_INIT_QOM のタイプに属するモジュールリスト ModuleTypeList (前回解析) があり、その中に ModuleEntry 項目が 1 つずつ存在します。KVM もその 1 つで、もちろん他にもマシン、CPU など多くの項目があります。各項目を初期化する init 関数は、パラメータで表される関数 fn です。KVM モジュールの場合、その init 関数は kvm_type_init、Machine モジュールの場合、その init 関数は machine_register_types です。最後に、前回解析した module_call_init 関数では、ここで割り当てた初期化 fn を実際に呼び出して、実際の初期化作業を行っています。
次回はKVMの初期化関数kvm_type_initを見ていきます。