qemu的初始化框架
qemu启动需要初始化很多内容,为了方便维护和添加初始化函数,qemu有它自己的一种实现方式。
qemu的初始化时根据具体的模块类型,执行该类型的所有模块的初始化。有如下类型:
1 2 3 4 5 6 7 8 9 10 11
| 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;
|
有一个全局的模块类型数组init_type_list
,数组中每项对应一种类型的模块。每个模块有自己的初始化函数,模块间用ModuleTypeList
类型的链表组织,初始化就可以针对一种类型的模块,遍历其链表来完成该类型的所有模块的初始化:
1
| static ModuleTypeList init_type_list[MODULE_INIT_MAX];
|
qemu初始化框架的核心其实是使用编译器参数__attribute__((constructor))
,使得模块注册函数在main函数之前执行。不同类型模块的注册函数接口不同,但本质都是对register_module_init(function, type)
的封装,它会根据模块类型type
,将初始化函数function
添加到模块初始化链表末尾。
以type_init
为例,它是QOM类型模块的注册接口函数,其他类型的宏定义同理:
1 2 3 4 5 6 7 8 9 10
| #define type_init(function) module_init(function, MODULE_INIT_QOM) #define block_init(function) module_init(function, MODULE_INIT_BLOCK) #define opts_init(function) module_init(function, MODULE_INIT_OPTS) #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) #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)
|
type_init
向module_init
宏传入的type为MODULE_INIT_QOM
。根据这个枚举值就能在全局的init_type_list
数组中找到对应类型的模块链表。然后调用module_init
将对应的初始化函数,即参数function
,插入链表末尾:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define module_init(function, type) \ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ { \ register_dso_module_init(function, 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
会展开成一系列__attribute__((constructor)) do_qemu_init_ ## function(void)
函数。编译器参数__attribute__((constructor))
保证了这些初始化函数在main函数前执行,即完成模块注册。
这样一来在qemu主初始化函数qemu_init
中就可以在根据需要调用module_call_init(type)
执行对应类型模块的初始化了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }
|
一个实例
以memory_region_info
这个QOM对象为例,可以在源码memory.c
中找到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static const TypeInfo memory_region_info = { ... };
static const TypeInfo iommu_memory_region_info = { ... };
static void memory_register_types(void) { type_register_static(memory_region_info); ... }
type_init(memory_register_types)
|
QOM对象调用type_register_static()
,根据TypeInfo
结构完成类的创建。那么就需要调用type_init()
将类创建函数注册到模块链表中,确保初始化时创建好类。
type_init
通过宏展开最终将memory_register_types
展开成do_qemu_init_memory_register_types
这个在main函数执行前调用的函数。它再通过register_module_init
将memory_register_types
插入init_type_list[MODULE_INIT_QOM]
链表中。
这样QOM模块需要初始化时qemu_init
就能通过module_call_init(MODULE_INIT_QOM)
执行QOM类型模块的初始化函数,其中就包括刚才注册的memory_register_types()