接前一篇文章: QEMU源码全解析40 —— Machine(10)
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
时间过去了几个月,重开“Machine”系列……
“Machine”系列的前10篇文章、尤其是从第3篇文章开始,就感觉比较乱,一直在针对于宏定义进行展开,并且头绪众多,给人感觉没有一个清晰的脉络。那么本篇文章就来总结前边几篇文章的内容,并梳理出较为清晰的脉络。
先来看一张图(图片援引 《趣谈Linux系统》50 | 计算虚拟化之CPU(上):如何复用集团的人力资源? ):
在hw/i386/pc_piix.c中,有核心宏DEFINE_I440FX_MACHINE。对于每一个 QEMU 版本,都会定义一种新的机器类型,以笔者之前的v7.1以及现在使用的v8.1为例,代码分别如下:
- DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL,
- pc_i440fx_7_1_machine_options);
- DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL,
- pc_i440fx_8_1_machine_options);
DEFINE_I440FX_MACHINE宏的定义也在hw/i386/pc_piix.c中,如下:
- #define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
- static void pc_init_##suffix(MachineState *machine) \
- { \
- void (*compat)(MachineState *m) = (compatfn); \
- if (compat) { \
- compat(machine); \
- } \
- pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
- TYPE_I440FX_PCI_DEVICE); \
- } \
- DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
对于v7.1和v8.1,最终展开为:
- static void pc_init_v7_1(MachineState *machine)
- {
- void (*compat)(MachineState *m) = (NULL);
- if (compat) {
- compat(machine);
- }
- pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
- TYPE_I440FX_PCI_DEVICE);
- }
-
- static void pc_machine_v7_1_class_init(ObjectClass *oc, void *data)
- {
- MachineClass *mc = MACHINE_CLASS(oc);
- pc_i440fx_7_1_machine_options(mc);
- mc->init = pc_init_v7_1;
- }
- static const TypeInfo pc_machine_type_v7_1 = {
- .name = "pc-i440fx-7.1" TYPE_MACHINE_SUFFIX,
- .parent = TYPE_PC_MACHINE,
- .class_init = pc_machine_v7_1_class_init,
- };
- static void pc_machine_init_v7_1(void)
- {
- type_register(&pc_machine_type_v7_1);
- }
- type_init(pc_machine_init_v7_1)
和
- static void pc_init_v8_1(MachineState *machine)
- {
- void (*compat)(MachineState *m) = (NULL);
- if (compat) {
- compat(machine);
- }
- pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
- TYPE_I440FX_PCI_DEVICE);
- }
-
- static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
- {
- MachineClass *mc = MACHINE_CLASS(oc);
- pc_i440fx_8_1_machine_options(mc);
- mc->init = pc_init_v8_1;
- }
- static const TypeInfo pc_machine_type_v8_1 = {
- .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
- .parent = TYPE_PC_MACHINE,
- .class_init = pc_machine_v8_1_class_init,
- };
- static void pc_machine_init_v8_1(void)
- {
- type_register(&pc_machine_type_v8_1);
- }
- type_init(pc_machine_init_v8_1)
以v8.1为例,将上述代码分为四段,一一与上图对应起来:
- pc_machine_type_v8_1
- static const TypeInfo pc_machine_type_v8_1 = {
- .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
- .parent = TYPE_PC_MACHINE,
- .class_init = pc_machine_v8_1_class_init,
- };
对应
- pc_machine_v8_1_class_init
- static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
- {
- MachineClass *mc = MACHINE_CLASS(oc);
- pc_i440fx_8_1_machine_options(mc);
- mc->init = pc_init_v8_1;
- }
对应
- pc_init_v8_1
- static void pc_init_v8_1(MachineState *machine)
- {
- void (*compat)(MachineState *m) = (NULL);
- if (compat) {
- compat(machine);
- }
- pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
- TYPE_I440FX_PCI_DEVICE);
- }
对应
- pc_machine_init_v8_1
- static void pc_machine_init_v8_1(void)
- {
- type_register(&pc_machine_type_v8_1);
- }
- type_init(pc_machine_init_v8_1)
唯独这个pc_machine_init_v8_1图中没有明显对应对象,但根据type_init这个关键字,应对应图中这一部分:
再来回顾一下type_init,定义一个QEMU模块会调用type_init。type_init是一个宏,其定义在include/qemu/module.h中,如下:
#define type_init(function) module_init(function, MODULE_INIT_QOM)
因此,这里的
type_init(pc_machine_init_v8_1)
实际上是
- static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
- { \
- register_module_init(function, type); \
- }
module_init(pc_machine_init_v8_1, MODULE_INIT_QOM)
从代码中的定义就可以看出,type_init后边的参数是一个函数(function),调用type_init就相当于调用了module_init,而在这里的函数就是pc_machine_init_v8_1,类型就是MODULE_INIT_QOM。
module_init也是一个宏,其定义也在include/qemu/module.h中,如下:
- /* 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); \
- }
代入此处的实际值,为
- static void __attribute__((constructor)) do_qemu_init_pc_machine_init_v8_1(void)
- {
- register_module_init(pc_machine_init_v8_1, MODULE_INIT_QOM);
- }
由以上代码可知,module_init最终调用了register_module_init函数。register_module_init函数在util/module.c中,代码如下:
- static void init_lists(void)
- {
- static int inited;
- int i;
-
- if (inited) {
- return;
- }
-
- for (i = 0; i < MODULE_INIT_MAX; i++) {
- QTAILQ_INIT(&init_type_list[i]);
- }
-
- QTAILQ_INIT(&dso_init_list);
-
- inited = 1;
- }
-
- static ModuleTypeList *find_type(module_init_type type)
- {
- init_lists();
-
- return &init_type_list[type];
- }
-
- 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);
- }
module_init_type的定义在include/qemu/module.h中,如下:
- typedef enum {
- MODULE_INIT_MIGRATION,
- MODULE_INIT_BLOCK,
- MODULE_INIT_OPTS,
- MODULE_INIT_QOM,
- MODULE_INIT_TRACE,
- MODULE_INIT_XEN_BACKEND,
- MODULE_INIT_LIBQOS,
- MODULE_INIT_FUZZ_TARGET,
- MODULE_INIT_MAX
- } module_init_type;
属于MODULE_INIT_QOM这种类型的,有一个Module列表ModuleTypeList(就是上边代码中的init_type_list,其类型为ModuleTypeList),列表中是一项一项的ModuleEntry。register_module_init函数中会设置(初始化)每一项的init函数为函数参数中的fn(第1个参数)。此处的module的init函数就是pc_machine_init_v8_1。
- 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);
- }
当然,MODULE_INIT_QOM这种类型会有很多很多的module,所有调用type_init的地方都会注册一个MODULE_INIT_QOM类型的Module。
type_init也可以说register_module_init函数只是完成了注册,也即设置了该module的init函数指针所指向的回调函数,真正调用此回调函数的地方是在module_call_init函数中,该函数也在util/module.c中,代码如下:
- void module_call_init(module_init_type type)
- {
- ModuleTypeList *l;
- ModuleEntry *e;
-
- if (modules_init_done[type]) {
- return;
- }
-
- l = find_type(type);
-
- QTAILQ_FOREACH(e, l, node) {
- e->init();
- }
-
- modules_init_done[type] = true;
- }
在module_call_init函数中,会找到MODULE_INIT_QOM这种类型所对应的ModuleTypeList。
- static ModuleTypeList *find_type(module_init_type type)
- {
- init_lists();
-
- return &init_type_list[type];
- }
而后找出(该)列表中所有的ModuleEntry,然后调用每个ModuleEntry的init函数。对应的就是module_call_init函数中的这一代码片段:
- QTAILQ_FOREACH(e, l, node) {
- e->init();
- }
这里需要注意的是,在module_call_init函数调用的这一步,所有Module的init函数都已经被调用过了。也就是说,后文书会看到很多的Module,当看到它们的时候需要意识到,其init函数在此处已被调用过了。
对于这里的
type_init(pc_machine_init_v8_1)
module_call_init函数中的
e->init();
实际上调用的就是register_module_init函数中设置的
e->init = fn;
也就是pc_machine_init_v8_1函数。
- static void pc_machine_init_v8_1(void)
- {
- type_register(&pc_machine_type_v8_1);
- }
- type_init(pc_machine_init_v8_1)
对于type_register函数的详细解析及这条主线的梳理,放在“下半场”即下一回中。