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.h1 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.c1 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.h1 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.c1 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,其中ModuleEntry的init被初始化为
kvm_type_init, 进而注册了kvm_accel_type这个module的信息。
类似的,在QEMU运行的时候只要挨着调用这些List里面Entry的 .init函数就会挨着初始化所有的Module.