接前一篇文章: QEMU源码全解析12 —— QOM介绍(1)
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
本回开始对QOM涉及的各个方面进行深入细致的解析。
1. 类型的注册
在面向对象思想中,说到对象时,都会提到它所属的类。 QEMU 也需要实现一个类型系统。以hw/misc/edu.c文件为例(前文是以accel/kvm/kvm-all.c为例),这本身不是一个实际的设备,而是教学用的设备,其结构简单,比较清楚地展示了QEMU中的模拟设备。类型的注册是通过type_init完成的。代码如下:
- static void pci_edu_register_types(void)
- {
- static InterfaceInfo interfaces[] = {
- { INTERFACE_CONVENTIONAL_PCI_DEVICE },
- { },
- };
- static const TypeInfo edu_info = {
- .name = TYPE_PCI_EDU_DEVICE,
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(EduState),
- .instance_init = edu_instance_init,
- .class_init = edu_class_init,
- .interfaces = interfaces,
- };
-
- type_register_static(&edu_info);
- }
- 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中,如下所示:
- #define block_init(function) module_init(function, MODULE_INIT_BLOCK)
- #define opts_init(function) module_init(function, MODULE_INIT_OPTS)
- #define type_init(function) module_init(function, MODULE_INIT_QOM)
- #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
- #define xen_backend_init(function) module_init(function, \
- MODULE_INIT_XEN_BACKEND)
- #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
- #define fuzz_target_init(function) module_init(function, \
- MODULE_INIT_FUZZ_TARGET)
每一个宏定义都表示一类module,均通过module_init按照不同的参数构造出来。前文已给出过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宏,module_init有不同的定义,这里假设未定义该宏,则module_init的定义如下(前文也是这么讲的):
- /* 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); \
- }
可以看到各个QOM类型都是通过register_module_init函数注册到了系统。其中function是每个类型独立的,即每个类型都需要自行单独实现的初始化函数。type是MODULE_INIT_QOM。这里的constructor是编译器属性,编译器会把带有这个属性的函数do_qemu_init_ ## function放到特殊的段中,带有这个属性的函数会早于主函数main()执行,也就是说所有的QOM类型注册在main执行之前就已经执行了。register_module_init及相关函数代码如下(util/module.c中):
- 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);
- }
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]这个链表中。
欲知后事如何,且看下回分解。