QEMU源码全解析13 —— QOM介绍(2)

88 篇文章 19 订阅
本文深入解析QEMU中的QOM类型注册,通过`type_init`宏在main函数执行前将各类型注册到系统。以hw/misc/edu.c为例,介绍了`register_module_init`函数在`module_init`宏中的作用,确保QOM类型的初始化函数在程序启动时自动执行。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析12 —— QOM介绍(1)

本文内容参考:

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

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

特此致谢!

本回开始对QOM涉及的各个方面进行深入细致的解析。

1. 类型的注册

在面向对象思想中,说到对象时,都会提到它所属的类。 QEMU 也需要实现一个类型系统。以hw/misc/edu.c文件为例(前文是以accel/kvm/kvm-all.c为例),这本身不是一个实际的设备,而是教学用的设备,其结构简单,比较清楚地展示了QEMU中的模拟设备。类型的注册是通过type_init完成的。代码如下:

  1. static void pci_edu_register_types(void)
  2. {
  3. static InterfaceInfo interfaces[] = {
  4. { INTERFACE_CONVENTIONAL_PCI_DEVICE },
  5. { },
  6. };
  7. static const TypeInfo edu_info = {
  8. .name = TYPE_PCI_EDU_DEVICE,
  9. .parent = TYPE_PCI_DEVICE,
  10. .instance_size = sizeof(EduState),
  11. .instance_init = edu_instance_init,
  12. .class_init = edu_class_init,
  13. .interfaces = interfaces,
  14. };
  15. type_register_static(&edu_info);
  16. }
  17. type_init(pci_edu_register_types)

在include/qemu/module.h中可以看到,type_init是一个宏(实际上前文已讲过,但此处是从QOM的角度再讲一次,算作是一次回顾和巩固吧,下同),并且除了type_init,还有其它几个init宏,如block_init、opts_init、trace_init等,在include/qemu/module.h中,如下所示:

  1. #define block_init(function) module_init(function, MODULE_INIT_BLOCK)
  2. #define opts_init(function) module_init(function, MODULE_INIT_OPTS)
  3. #define type_init(function) module_init(function, MODULE_INIT_QOM)
  4. #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
  5. #define xen_backend_init(function) module_init(function, \
  6. MODULE_INIT_XEN_BACKEND)
  7. #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
  8. #define fuzz_target_init(function) module_init(function, \
  9. MODULE_INIT_FUZZ_TARGET)

每一个宏定义都表示一类module,均通过module_init按照不同的参数构造出来。前文已给出过module_init的代码,为了便于理解和回顾,再次贴出代码,在同文件(include/qemu/module.h)中,如下所示:

  1. #ifdef BUILD_DSO
  2. void DSO_STAMP_FUN(void);
  3. /* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can
  4. * distinguish "version mismatch" from "not a QEMU module", when the stamp
  5. * check fails during module loading */
  6. void qemu_module_dummy(void);
  7. #define module_init(function, type) \
  8. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  9. { \
  10. register_dso_module_init(function, type); \
  11. }
  12. #else
  13. /* This should not be used directly. Use block_init etc. instead. */
  14. #define module_init(function, type) \
  15. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  16. { \
  17. register_module_init(function, type); \
  18. }
  19. #endif

根据是否定义了BUILD_DSO宏,module_init有不同的定义,这里假设未定义该宏,则module_init的定义如下(前文也是这么讲的):

  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. }

可以看到各个QOM类型都是通过register_module_init函数注册到了系统。其中function是每个类型独立的,即每个类型都需要自行单独实现的初始化函数。type是MODULE_INIT_QOM。这里的constructor是编译器属性,编译器会把带有这个属性的函数do_qemu_init_  ## function放到特殊的段中,带有这个属性的函数会早于主函数main()执行,也就是说所有的QOM类型注册在main执行之前就已经执行了。register_module_init及相关函数代码如下(util/module.c中):

  1. static ModuleTypeList *find_type(module_init_type type)
  2. {
  3. init_lists();
  4. return &init_type_list[type];
  5. }
  6. void register_module_init(void (*fn)(void), module_init_type type)
  7. {
  8. ModuleEntry *e;
  9. ModuleTypeList *l;
  10. e = g_malloc0(sizeof(*e));
  11. e->init = fn;
  12. e->type = type;
  13. l = find_type(type);
  14. QTAILQ_INSERT_TAIL(l, e, node);
  15. }

register_module_init函数以类型的初始化函数(第1个参数void (*fn)(void))及所属类型(第2个参数type,对于QOM类型来说是MODULE_INIT_QOM)构建出一个ModuleEntry,然后插入到对应的module(对于QOM来说是MODULE_INIT_QOM这个module)所属的链表中。所有module的链表存放在一个init_type_list数组中。init_type_list与各个module以及ModuleEntry之间的关系简图如下所示:

综上可知,QEMU使用的各个类型在main函数执行之前就统一注册到了init_type_list[MODULE_INIT_QOM]这个链表中。

欲知后事如何,且看下回分解。

举报

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