接前一篇文章:QEMU源码全解析7 —— QEMU主函数
上一篇文章讲到了qemu_init函数中的module_call_init(MODULE_INIT_OPTS)。本篇文章对这个函数进行解析。
module_call_init函数在util/module.c中,代码如下:
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;
}
QEMU作为中间人其实还是蛮辛苦的,对上面的虚拟机需要模拟各种各样的外部设备;而当虚拟机实际要使用物理资源的时候,对下面的物理机上的资源要进行请求。其工作模式有点儿类似于操作系统对接驱动。驱动要按照(符合)一定的(要求的)格式,才能被操作系统所接受,成为操作系统的一个模块。同理,QEMU为了模拟各类设备,也需要管理各种各样的模块,这些模块也要符合一定的格式。
由于这里传给module_init_call函数的参数值是MODULE_INIT_OPTS,因此在module_call_init中,会找到MODULE_INIT_OPTS这种类型对应的ModuleTypeList,找出列表中所有的ModuleEntry,然后调用每个ModuleEntry的init函数。
MODULE_INIT_OPTS的定义在include/qemu/module.h中,代码如下:
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;
ModuleTypeList的定义同样在util/module.c中:
typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
QTAILQ_HEAD的定义在include/qemu/queue.h中,代码如下:
typedef struct QTailQLink {
void *tql_next;
struct QTailQLink *tql_prev;
} QTailQLink;
/*
* Tail queue definitions. The union acts as a poor man template, as if
* it were QTailQLink<type>.
*/
#define QTAILQ_HEAD(name, type) \
union name { \
struct type *tqh_first; /* first element */ \
QTailQLink tqh_circ; /* link for circular backwards list */ \
}
因此,QTAILQ_HEAD的定义实际展开为:
typedef union {
struct ModuleEntry *tqh_first;
QTailQLink tqh_circ;
} ModuleTypeList;
module_call_init函数中ModuleEntry的定义同样在util/module.c中,代码如下:
typedef struct ModuleEntry
{
void (*init)(void);
QTAILQ_ENTRY(ModuleEntry) node;
module_init_type type;
} ModuleEntry;
QTAILQ_ENTRY的定义和QTAILQ_HEAD的定义一样,都在include/qemu/queue.h中,代码如下:
#define QTAILQ_ENTRY(type) \
union { \
struct type *tqe_next; /* next element */ \
QTailQLink tqe_circ; /* link for circular backwards list */ \
}
而ModuleEntry结构中module_init_type的定义上边已经给出了。
这样,整个数据结构就比较清晰了。结合数据结构来看函数中的具体功能代码。
find_type函数在util/module.c中,代码如下:
static ModuleTypeList *find_type(module_init_type type)
{
init_lists();
return &init_type_list[type];
}
而init_lists函数就在上边,代码如下:
static void init_lists(void)
{
static int inited;
int i;
if (inited) {
return;
}
for (i = 0; i < MODULE_INIT_MAX; i++) {
QTAILQ_INIT(&init_type_list[i]);
}
QTAILQ_INIT(&dso_init_list);
inited = 1;
}
MODULE_INIT_MAX在上边的module_init_type定义中,值为8。
init_type_list和dso_init_list都在init_lists函数的上边:
static ModuleTypeList init_type_list[MODULE_INIT_MAX];
static bool modules_init_done[MODULE_INIT_MAX];
static ModuleTypeList dso_init_list;
而QTAILQ_INIT的定义和QTAILQ_HEAD的定义一样,都在include/qemu/queue.h中,代码如下:
/*
* Tail queue functions.
*/
#define QTAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_circ.tql_prev = &(head)->tqh_circ; \
} while (/*CONSTCOND*/0)
在module_call_init函数中,会找到参数type(类型,如:MODULE_INIT_OPTS、MODULE_INIT_QOM)所对应的ModuleTypeList,找出列表中所有的ModuleEntry,然后调用每个ModuleEntry的init函数。
这里需要注意:在module_call_init调用的这一步,所有Module的init函数都已经被调用过了。