QEMU在main函数前对模块的初始化过程
2016-12-12 12:50
183 查看
初始化的难题
QEMU中包含了大量的初始化函数,比如使用QOM模型设计的很多类(CPU、设备等都是利用QOM模型设计实现模拟的),这些类需要注册到管理类型的全局的hash表中,这个注册的过程需要在初始化函数中完成。想象一下,如果我们把这些注册过程都放到main函数里面调用,main函数中就会有非常长的一段篇幅,仅仅是用于调用大量的初始化函数,这样对于QEMU的代码维护非常不利,因此QEMU将这些初始化函数的指针保存到了链表数据结构中,这样只要遍历一遍链表就可以执行全部的初始化函数。说起来简单,但是其中的设计和实现,以及对GCC中相关特性的使用都有我们值得学习和研究的地方。
模块化管理初始化工作
QEMU中的代码的初始化管理是分模块的,实现这种模块化的代码文件包括include/qemu/module.h 和util/module.c。在include/qemu/module.h中所涉及的相关的代码模块包括:block (QEMU中的块操作实现代码)
machine (QEMU中模拟设备的实现代码模块)
qapi (QEMU向上层调用者提供的接口的代码模块)
type 或者qom(QEMU中的QOM模型所涉及的代码模块)
重要的全局变量: init_type_list
该变量的定义如下://这个数据结构保存了某种类型的代码模块的一个初始化函数中指针 typedef struct ModuleEntry { void (*init)(void); QTAILQ_ENTRY(ModuleEntry) node; module_init_type type; } ModuleEntry; typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; //其中MODULE_INIT_MAX为4,也就是4种模块,每一种对应一个ModuleTypeList //每个ModuleTypeList将保存一种代码模块中所有初始化函数指针 static ModuleTypeList init_type_list[MODULE_INIT_MAX];
qemu中的module实现了QEMU在main函数之前需要执行的大量函数
首先普及一下gcc的一些知识
gcc中的构造函数属性和析构函数属性
gcc为函数提供了几种类型的属性,其中就包括构造函数(contructors),用户可以定义一个函数:static void __attribute__((constructor)) start(void)
这样start函数就会在main函数执行之前执行。
同理gcc中也有__attribute__((destructor)),会在main函数之后执行。
gcc中宏的##和#扩展符
在gcc的宏中,#扩展符可以将宏字符串化,##扩展符可以将它左右两边连接起来。比如:#define SSVAR(X,Y) const char X[]=#Y SSVAR(InternetGatewayDevice, InternetGatewayDevice.);
等价于如下代码:
const char InternetGatewayDevice[]="InternetGatewayDevice.";
又比如:
#define DEV_FILE_NAME "/dev/test_kft" #define OPEN_FILE(fd, n) \ { \ fd = open(DEV_FILE_NAME ##n, 0); \ if (fd < 0) \ { \ printf("Open device error/n"); \ return 0; \ } \ } OPEN_FILE(fd1, 1); OPEN_FILE(fd2, 2);
它等价于:
{ fd1 = open(DEV_FILE_NAME1, 0); if (fd1 < 0) { printf("Open device error/n"); return 0; } }; { fd2 = open(DEV_FILE_NAME2, 0); if (fd2 < 0) { printf("Open device error/n"); return 0; } };
言归正传
要想看QEMU在main函数之前做了什么,可以直接查找__attribute__((constructor))。(感兴趣的读者可以自行查找)通过查找,我们可以发现只有少数几个文件中定义了具有constructor属性的函数,其中就包括include/qemu/module.h文件中do_qemu_init_ ## function(void),它是一个宏,而不是一个具体的函数。它的定义如下:(代码在include/qemu/module.h中)
#define module_init(function, type) \ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ { \ register_module_init(function, type); \ } #endif
我们进一步看一下register_module_init的实现:(代码在util/module.c中)。这段代码实际上是将类型为type的代码模块中一个初始化函数的指针存入全局变量init_type_list对应类型的链表中。
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); //找到全局变量init_type_list类型为type的ModuleTypeList链表 QTAILQ_INSERT_TAIL(l, e, node); }
这将意味着,如果在全局的代码中调用module_init(f, t),系统就会定义一个名为module_init_f的函数,并且该函数是constructor,必须在main函数之前执行,该函数要执行的代码就是下列代码。这个函数最终将函数指针f存入全局变量init_type_list数组中的type为t的链表中。
register_module_init(f, t);
而根据这个module_init宏,qemu分别针对四个代码模块定义了四种宏:
typedef enum { MODULE_INIT_BLOCK, MODULE_INIT_MACHINE, MODULE_INIT_QAPI, MODULE_INIT_QOM, MODULE_INIT_MAX } module_init_type; #define block_init(function) module_init(function, MODULE_INIT_BLOCK) #define machine_init(function) module_init(function, MODULE_INIT_MACHINE) #define qapi_init(function) module_init(function, MODULE_INIT_QAPI) #define type_init(function) module_init(function, MODULE_INIT_QOM)
也就是说,在全局的代码中如果调用上述任何一个函数,就会在系统中自动生成一个具有constructor属性的函数,这个函数会在main函数之前执行。举一个例子,我们在qom/object.c中看到了type_init()函数的一个调用:
static void register_types(void) { static TypeInfo interface_info = { .name = TYPE_INTERFACE, .class_size = sizeof(InterfaceClass), .abstract = true, }; static TypeInfo object_info = { .name = TYPE_OBJECT, .instance_size = sizeof(Object), .instance_init = object_instance_init, .abstract = true, }; type_interface = type_register_internal(&interface_info); type_register_internal(&object_info); } type_init(register_types)
这样就会在系统中生成一个如下函数:
static void __attribute__((constructor)) do_qemu_init_register_type(void) { register_module_init(register_type, MODULE_INIT_QOM); }
qemu中如何利用module实现大量代码的初始化工作
通过上述定义constructor属性的函数的方式,qemu会在main函数之前,将所有代码模块中的所有的初始化函数指针保存到init_type_list数组中的对应类型的链表中。我们只需调用每种类型链表中每个entry保存的初始化函数指针,就可以实现调用所有代码模块中大量的初始化函数指针。void module_call_init(module_init_type type) { ModuleTypeList *l; ModuleEntry *e; module_load(type); l = find_type(type); QTAILQ_FOREACH(e, l, node) { e->init(); } }
总结
qemu中通过设计module的机制,将大量的初始化函数分模块地保存到链表中,简化了这些代码模块的初始化工作,也简化了实现的流程,提高了代码的可维护性。相关文章推荐
- day06static关键字,main函数,帮助文档的制作,静态代码块,对象的初始化过程,单例设计模式
- ABP中模块初始化过程(二)
- Qemu-KVM虚拟机初始化及创建过程源码简要分析(二)
- Qemu-KVM虚拟机初始化及创建过程源码简要分析(一)
- FreeSwitch 的初始化及其模块加载过程
- ucos在s3c2410上运行过程整体剖析-从main函数到UCOS初始化完毕 分类: μc /os ii 系统有关知识 2012-03-13 21:37 1740人阅读 评论(0) 收藏
- SpingMVC模块常用几种handlerMapping的初始化过程
- SpringAOP模块初始化过程
- ucos在s3c2410上运行过程整体剖析-从main函数到UCOS初始化完毕
- FreeSwitch 的初始化及其模块加载过程
- Spring AOP模块初始化过程
- ucos在s3c2410上运行过程整体剖析-从main函数到UCOS初始化完毕
- 嵌入式视频方案学习第十二篇——视频编码模块VENC 一般初始化过程
- nginx源码分析(2)——http模块的初始化过程
- MFC dll的初始化过程及关于模块状态的错误
- ABP中的模块初始化过程(一)
- nginx源码分析(2)——http模块的初始化过程
- http模块初始化过程
- 嵌入式中main函数起来之前,startup要完成的初始化过程
- FreeSwitch 的初始化及其模块加载过程