接前一篇文章: QEMU源码全解析7 —— QEMU主函数
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一篇文章讲到了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函数都已经被调用过了。