接前一篇文章: QEMU源码全解析8 —— 初始化所有Module
本文内容参考:
《趣谈 Linux操作系统 》 —— 刘超,极客时间
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
定义一个 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)来说,最终要调用到register_module_init(kvm_type_init, MODULE_INIT_QOM),也就是MODULE_INIT_QOM这种类型。属于MODULE_INIT_QOM这种类型的,有一个Module列表ModuleTypeList(上一回已分析过),列表里边是一项一项的ModuleEntry。KVM就是其中的一项,当然还有很多其他项,比如Machine、CPU等。会 初始化 每一项的init函数为参数表示的函数fn,对于KVM这个module来说,其init函数就是kvm_type_init;而对于Machine这个module来说,其init函数为machine_register_types。最终在上一回分析的module_call_init函数中实际调用这里赋值的初始化fn,进行实际的初始化工作。
下一回我们就来看看KVM的这个初始化函数kvm_type_init。