接前一篇文章:QEMU源码全解析12 —— QOM介绍(1)
本文内容参考:
《趣谈Linux操作系统》 —— 刘超,极客时间
《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]这个链表中。
欲知后事如何,且看下回分解。