QEMU源码全解析41 —— Machine(11)

88 篇文章 19 订阅
本文总结了QEMU Machine系列前几篇文章的内容,梳理了Machine类型的定义和初始化过程。通过分析QEMU v7.1和v8.1版本的代码,解释了DEFINE_I440FX_MACHINE宏的展开,以及type_init和module_init宏在模块初始化中的作用。文章还探讨了module_call_init函数如何调用各个ModuleEntry的init函数,为理解QEMU的内部工作原理提供了清晰的脉络。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析40 —— Machine(10)

本文内容参考:

《趣谈 Linux操作系统 》 —— 刘超, 极客时间

QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社

特此致谢!

时间过去了几个月,重开“Machine”系列……

“Machine”系列的前10篇文章、尤其是从第3篇文章开始,就感觉比较乱,一直在针对于宏定义进行展开,并且头绪众多,给人感觉没有一个清晰的脉络。那么本篇文章就来总结前边几篇文章的内容,并梳理出较为清晰的脉络。

先来看一张图(图片援引 《趣谈Linux系统》50 | 计算虚拟化之CPU(上):如何复用集团的人力资源? ):

在hw/i386/pc_piix.c中,有核心宏DEFINE_I440FX_MACHINE。对于每一个 QEMU 版本,都会定义一种新的机器类型,以笔者之前的v7.1以及现在使用的v8.1为例,代码分别如下:

  1. DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL,
  2. pc_i440fx_7_1_machine_options);
  1. DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL,
  2. pc_i440fx_8_1_machine_options);

DEFINE_I440FX_MACHINE宏的定义也在hw/i386/pc_piix.c中,如下:

  1. #define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
  2. static void pc_init_##suffix(MachineState *machine) \
  3. { \
  4. void (*compat)(MachineState *m) = (compatfn); \
  5. if (compat) { \
  6. compat(machine); \
  7. } \
  8. pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
  9. TYPE_I440FX_PCI_DEVICE); \
  10. } \
  11. DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)

对于v7.1和v8.1,最终展开为:

  1. static void pc_init_v7_1(MachineState *machine)
  2. {
  3. void (*compat)(MachineState *m) = (NULL);
  4. if (compat) {
  5. compat(machine);
  6. }
  7. pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
  8. TYPE_I440FX_PCI_DEVICE);
  9. }
  10. static void pc_machine_v7_1_class_init(ObjectClass *oc, void *data)
  11. {
  12. MachineClass *mc = MACHINE_CLASS(oc);
  13. pc_i440fx_7_1_machine_options(mc);
  14. mc->init = pc_init_v7_1;
  15. }
  16. static const TypeInfo pc_machine_type_v7_1 = {
  17. .name = "pc-i440fx-7.1" TYPE_MACHINE_SUFFIX,
  18. .parent = TYPE_PC_MACHINE,
  19. .class_init = pc_machine_v7_1_class_init,
  20. };
  21. static void pc_machine_init_v7_1(void)
  22. {
  23. type_register(&pc_machine_type_v7_1);
  24. }
  25. type_init(pc_machine_init_v7_1)

  1. static void pc_init_v8_1(MachineState *machine)
  2. {
  3. void (*compat)(MachineState *m) = (NULL);
  4. if (compat) {
  5. compat(machine);
  6. }
  7. pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
  8. TYPE_I440FX_PCI_DEVICE);
  9. }
  10. static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
  11. {
  12. MachineClass *mc = MACHINE_CLASS(oc);
  13. pc_i440fx_8_1_machine_options(mc);
  14. mc->init = pc_init_v8_1;
  15. }
  16. static const TypeInfo pc_machine_type_v8_1 = {
  17. .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
  18. .parent = TYPE_PC_MACHINE,
  19. .class_init = pc_machine_v8_1_class_init,
  20. };
  21. static void pc_machine_init_v8_1(void)
  22. {
  23. type_register(&pc_machine_type_v8_1);
  24. }
  25. type_init(pc_machine_init_v8_1)

以v8.1为例,将上述代码分为四段,一一与上图对应起来:

  • pc_machine_type_v8_1
  1. static const TypeInfo pc_machine_type_v8_1 = {
  2. .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
  3. .parent = TYPE_PC_MACHINE,
  4. .class_init = pc_machine_v8_1_class_init,
  5. };

对应

  • pc_machine_v8_1_class_init
  1. static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
  2. {
  3. MachineClass *mc = MACHINE_CLASS(oc);
  4. pc_i440fx_8_1_machine_options(mc);
  5. mc->init = pc_init_v8_1;
  6. }

对应

  • pc_init_v8_1
  1. static void pc_init_v8_1(MachineState *machine)
  2. {
  3. void (*compat)(MachineState *m) = (NULL);
  4. if (compat) {
  5. compat(machine);
  6. }
  7. pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
  8. TYPE_I440FX_PCI_DEVICE);
  9. }

对应

  • pc_machine_init_v8_1
  1. static void pc_machine_init_v8_1(void)
  2. {
  3. type_register(&pc_machine_type_v8_1);
  4. }
  5. 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)

实际上是

  1. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  2. { \
  3. register_module_init(function, type); \
  4. }
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中,如下:

  1. /* This should not be used directly. Use block_init etc. instead. */
  2. #define module_init(function, type) \
  3. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  4. { \
  5. register_module_init(function, type); \
  6. }

代入此处的实际值,为

  1. static void __attribute__((constructor)) do_qemu_init_pc_machine_init_v8_1(void)
  2. {
  3. register_module_init(pc_machine_init_v8_1, MODULE_INIT_QOM);
  4. }

由以上代码可知,module_init最终调用了register_module_init函数。register_module_init函数在util/module.c中,代码如下:

  1. static void init_lists(void)
  2. {
  3. static int inited;
  4. int i;
  5. if (inited) {
  6. return;
  7. }
  8. for (i = 0; i < MODULE_INIT_MAX; i++) {
  9. QTAILQ_INIT(&init_type_list[i]);
  10. }
  11. QTAILQ_INIT(&dso_init_list);
  12. inited = 1;
  13. }
  14. static ModuleTypeList *find_type(module_init_type type)
  15. {
  16. init_lists();
  17. return &init_type_list[type];
  18. }
  19. void register_module_init(void (*fn)(void), module_init_type type)
  20. {
  21. ModuleEntry *e;
  22. ModuleTypeList *l;
  23. e = g_malloc0(sizeof(*e));
  24. e->init = fn;
  25. e->type = type;
  26. l = find_type(type);
  27. QTAILQ_INSERT_TAIL(l, e, node);
  28. }

module_init_type的定义在include/qemu/module.h中,如下:

  1. typedef enum {
  2. MODULE_INIT_MIGRATION,
  3. MODULE_INIT_BLOCK,
  4. MODULE_INIT_OPTS,
  5. MODULE_INIT_QOM,
  6. MODULE_INIT_TRACE,
  7. MODULE_INIT_XEN_BACKEND,
  8. MODULE_INIT_LIBQOS,
  9. MODULE_INIT_FUZZ_TARGET,
  10. MODULE_INIT_MAX
  11. } 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。

  1. void register_module_init(void (*fn)(void), module_init_type type)
  2. {
  3. ModuleEntry *e;
  4. ModuleTypeList *l;
  5. e = g_malloc0(sizeof(*e));
  6. e->init = fn;
  7. e->type = type;
  8. l = find_type(type);
  9. QTAILQ_INSERT_TAIL(l, e, node);
  10. }

当然,MODULE_INIT_QOM这种类型会有很多很多的module,所有调用type_init的地方都会注册一个MODULE_INIT_QOM类型的Module。

type_init也可以说register_module_init函数只是完成了注册,也即设置了该module的init函数指针所指向的回调函数,真正调用此回调函数的地方是在module_call_init函数中,该函数也在util/module.c中,代码如下:

  1. void module_call_init(module_init_type type)
  2. {
  3. ModuleTypeList *l;
  4. ModuleEntry *e;
  5. if (modules_init_done[type]) {
  6. return;
  7. }
  8. l = find_type(type);
  9. QTAILQ_FOREACH(e, l, node) {
  10. e->init();
  11. }
  12. modules_init_done[type] = true;
  13. }

在module_call_init函数中,会找到MODULE_INIT_QOM这种类型所对应的ModuleTypeList。

  1. static ModuleTypeList *find_type(module_init_type type)
  2. {
  3. init_lists();
  4. return &init_type_list[type];
  5. }

而后找出(该)列表中所有的ModuleEntry,然后调用每个ModuleEntry的init函数。对应的就是module_call_init函数中的这一代码片段:

  1. QTAILQ_FOREACH(e, l, node) {
  2. e->init();
  3. }

这里需要注意的是,在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函数。

  1. static void pc_machine_init_v8_1(void)
  2. {
  3. type_register(&pc_machine_type_v8_1);
  4. }
  5. type_init(pc_machine_init_v8_1)

对于type_register函数的详细解析及这条主线的梳理,放在“下半场”即下一回中。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他