0. GCC的attribute关键字
这是GCC的一个特性,gcc可以使用attribute关键字,格式如下:
__attribute__((attribute_name))
其中attribute_name中有两类constructor
和destructor
类似C++中类的构造和析构的概念,只不过是相对main()函数来说的。简单说,__attribute__((constructor))
定义的函数在main前执行,__attribute__((destructor))
定义的函数在main后执行。
1. 以qcow2为例
QEMU中有很多”module”的初始化使用了__attribute__((constructor))
这个特性,来在main前完成init函数的注册过程。使用方法举例具体如下:
// QEMU_2.10_SRC/include/qemu/module.h中:
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_module_init(function, type); \
}
#endif
typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_OPTS,
MODULE_INIT_QOM,
MODULE_INIT_TRACE,
MODULE_INIT_MAX
} module_init_type;
// 比如这里block_init函数被用在QEMU_SRC/block/*的qcow2等Format Driver中广泛应用,
// 其实就是间接调用了被__attribute__((constructor))调用的register_module_init()
#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)
下面以qcow2这个模块的初始化为例介绍下:
Step 1. main函数前执行constructor
第一步利用gcc支持的“构造函数”,将qcow2的初始化函数“注册”:
// qcow2 的init函数的注册流程
block_init(bdrv_qcow2_init) ------------------------ qcow2.c
----+----- -----+---------
| |
| v
| bdrv_register(&bdrv_qcow2)
v
#define block_init(function) module_init(function, MODULE_INIT_BLOCK) ---------- include/qemu/module.h
----+-------
|
v
register_module_init(function, type)
|
v
l = find_type(type)
e = g_malloc0(sizeof(*e));
e->init = fn;
e->type = type;
QTAILQ_INSERT_TAIL(l, e, node); ------------------------- util/module.c
util/module.c
中有这样一句:static ModuleTypeList init_type_list[MODULE_INIT_MAX];
可以看到在这之后最终函数指针fn
通过变量e
和宏函数QTAILQ_INSERT_TAIL(l, e, node)被存入事先定义的全局数组init_type_list中的type链表(这里type是MODULE_INIT_BLOCK)中。我们继续看main函数:
Step 2. main函数中进行init
调用流程如下:
main() ----------------- vl.c
|
v
bdrv_init_with_whitelist() ----------------- block.c
|
v
bdrv_init()
|
v
module_call_init(MODULE_INIT_BLOCK) ---------------- util/module.c
|
v
l = find_type(MODULE_INIT_BLOCK) --> QTAILQ_FOREACH(e, l, node){e->init()}
我们看到虽然module_call_init(MODULE_INIT_BLOCK)
和上边step 1.中的register_module_init(function, type)
函数都在util/module.c
中,但是调用的时机是不同的,step1中的register注册模块,step2中的call才是在真正调用init函数,而step1是在main前执行的,step2是在main函数中执行的。
[1] __ATTRIBUTE__ 你知多少?, http://www.cnblogs.com/astwish/articles/3460618.html
[2] QEMU在main函数前对模块的初始化过程, http://blog.csdn.net/u011364612/article/details/53581501