KVM Log 1-QEMU Object Module (QOM)

KVM分为两部分,User态部分在Qemu中,Kernel态部分集成到了Linux的代码里作为Kernel Module的形式对上提供服务。由于整个大的流程还是在QEMU里面的,所以本篇先介绍一下设备注册的相关信息。

Reference: Mctrain’s Blog: Qemu中的设备注册

QEMU Object Module

关于QOM(Qemu Object Module)的介绍

其发展如下图(从Mactrain’s Blog里面摘过来的,原来的PDF链接失效了)

kvm-all.c文件的最后,我们可以发现有个type_init(kvm_type_init), 其中type_init的定义在

include/qemu/module.h
1
2
3
4
5
6
7
8
9
35 #define module_init(function, type)                                         \
36 static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
37 { \
38 register_module_init(function, type); \
39 }

52 #define type_init(function) module_init(function, MODULE_INIT_QOM)
53 #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
...

可以发现最终会变成do_qemu_init_kvm_type_init(void) { ... },其中 register_module_init的实现在对应的C文件↓

util/module.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
33 static ModuleTypeList init_type_list[MODULE_INIT_MAX];
36
37 static void init_lists(void)
38 {
39 static int inited;
40 int i;
41
42 if (inited)
43 return;
45
46 for (i = 0; i < MODULE_INIT_MAX; i++) {
47 QTAILQ_INIT(&init_type_list[i]);
48 }
49
50 QTAILQ_INIT(&dso_init_list);
52 inited = 1;
53 }

56 static ModuleTypeList *find_type(module_init_type type)
57 {
58 init_lists();
60 return &init_type_list[type];
61 }

63 void register_module_init(void (*fn)(void), module_init_type type)
64 {
65 ModuleEntry *e;
66 ModuleTypeList *l;
67
68 e = g_malloc0(sizeof(*e));
69 e->init = fn;
70 e->type = type;
71
72 l = find_type(type);
73
74 QTAILQ_INSERT_TAIL(l, e, node);
75 }

find_type会调用init_lists进行链表的初始化。

register_module_init里面会malloc一个新的ModuleEntry并插入对应init_type_list[Module_Init_Type]链表中, Entry的初始化函数指针init和类型type会被赋值为传进来的参数。 Module_Init_Type稍后再说,这里主要强调一下__attribute__((constructor)),这个是GCC提供的特性,能够让函数在 main()执行前被执行。所以这些代码都在main()之前执行了!

关于__attribute__((constructor))的介绍可以参考这篇: C Language Constructors and Destructors with GCC

因此在QEMU运行之前,全局的所有Module对应的Entry就已经被插入到init_type_list中。

KVM_accel_module

include/qemu/module.h
1
2
3
4
5
6
7
42 typedef enum {
43 MODULE_INIT_BLOCK,
44 MODULE_INIT_OPTS,
45 MODULE_INIT_QOM,
46 MODULE_INIT_TRACE,
47 MODULE_INIT_MAX
48 } module_init_type;

Qemu里面一共就4种Module: BLOCK,OPT,QOM,TRACE,在include/qom/object.h中详细介绍了如何使用已有的接口来实现自己的Module_Init_Type, 所以根据上面的分析, 在Main函数执行之前,我们就获得了一个这样的链表↓:

1
2
3
4
5
6
7
8
9
10
11
init_type_list:                init_type_list[MODULE_INIT_QOM]:
↓ ↓
------------------- |----------> ModuleEntry-0
MODULE_INIT_BLOCK | ↑ ↓
------------------- | ModuleEntry-1
MODULE_INIT_OPTS | ↑ ↓
------------------- | ModuleEntry-2
MODULE_INIT_QOM ------| ↑ ↓
------------------- ......
MODULE_INIT_TRACE
-------------------

其中kvm_accel_type就是QOM的其中一个ModuleEntry:

accel/kvm/kvm-all.c
1
2
3
4
5
6
7
8
9
10
11
12
13
2615 static const TypeInfo kvm_accel_type = {
2616 .name = TYPE_KVM_ACCEL,
2617 .parent = TYPE_ACCEL,
2618 .class_init = kvm_accel_class_init,
2619 .instance_size = sizeof(KVMState),
2620 };
2621
2622 static void kvm_type_init(void)
2623 {
2624 type_register_static(&kvm_accel_type);
2625 }
2626
2627 type_init(kvm_type_init);

根据上面的分析,kvm 通过type_init这个洪在init_type_list[MODULE_INIT_QOM]链表中加入了一个自己的ModuleEntry,其中ModuleEntryinit被初始化为 kvm_type_init, 进而注册了kvm_accel_type这个module的信息。

类似的,在QEMU运行的时候只要挨着调用这些List里面Entry的 .init函数就会挨着初始化所有的Module.