qemu基本知识
前言
这里简单介绍一些QEMU相关的基本知识,从而方便后续更深入的研究QEMU
QOM(QEMU Object Model)
QEMU提供了一套面向对象编程的模型,从而实现各种具有继承关系的设备的模拟。
面向对象编程通常涵盖了类和对象这两个关键概念,其中类是对象的抽象定义,而对象则是类的具体实例。在QOM中,这些概念得到了实现和体现。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 struct OjbectProperty
┌────────────┐
│ │
└─────▲──────┘
│ ┌──┬──────────────┬──┐
│ │ │ class ├──┼──────►┌──┬──────────────┬──┐
│ │ ├──────────────┤ │ │ │ type ├──┼────►┌──────────────┐
◄─────────┴──────────┼──┤ properties │ │ │ ├──────────────┤ │ │ ◄─────┐
│ ├──────────────┤ │ ┌───┼──┤ properties │ │ └──────────────┘ │
│ │ ... │ │ │ │ ├──────────────┤ │ struct TypeImpl │
│ └──────────────┘ │ │ │ │ ... │ │ │
│ parent_obj │ │ │ └──────────────┘ │ │
├────────────────────┤ │ │ parent_class │ ┌───────────────┐ │
│ ...... │ │ ├────────────────────┤ │ │ │
└────────────────────┘ │ │ ...... │ └───────────────┘ │
struct DeviceState │ └────────────────────┘ struct TypeInfo────┘
│ struct DeviceClass
│
│
│
│ struct OjbectProperty
│ ┌────────────┐
├──────►│ │
│ └────────────┘
▼
类
QOM使用struct TypeInfo和Class结构体类型共同描述一个类。
其中struct TypeInfo描述类的基本属性,Class结构体类型描述静态成员。
struct TypeInfo
QOM中使用struct TypeInfo描述诸如类的名称、父类名称、实例大小和静态成员情况
1 | /** |
Class结构体
QOM使用用户自定义的Class结构体描述诸如函数表、静态成员等类的静态内容,因此所有的对象只能共享一份Class结构体和struct TypeInfo数据。
考虑到类会继承父类的成员内容,因此需要在Class结构体中包含父类的Class数据来实现继承关系。同时为了能安全的实现面向对象编程中的向上转型,总是将父类的数据放在Class结构体的最开始,如struct PCIDeviceClass所示。
1 | /* |
对象
QOM使用用户自定义的Object结构体描述非静态成员,也就是对象的数据,因此所有的对象都有自己的Object结构数据。
类似于Class结构体,考虑到对象同样会继承父类对象的成员内容,因此需要在Object结构体中包含父类的Object数据来实现继承,父类的数据同样应放在Object结构体的最开始以实现向上转型,如struct PCIDevice所示。
1 | /* |
初始化
类的初始化
根据前面章节的介绍,QOM使用struct TypeInfo和Class结构体共同来描述类,类的初始化也就是这两个数据的初始化,包括如下几个步骤
注册类
QOM使用type_init宏注册类信息,如下所示
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
38
39
40
41
42
43
44
45
46
47/*
* #0 register_module_init (fn=0x555555a9b5a8 <pci_register_types>, type=MODULE_INIT_QOM) at ../../qemu/util/module.c:75
* #1 0x0000555555a9b63c in do_qemu_init_pci_register_types () at ../../qemu/hw/pci/pci.c:2851
* #2 0x00007ffff7829ebb in call_init (env=<optimized out>, argv=0x7fffffffdc58, argc=29) at ../csu/libc-start.c:145
* #3 __libc_start_main_impl (main=0x555555e92809 <main>, argc=29, argv=0x7fffffffdc58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc48) at ../csu/libc-start.c:379
* #4 0x000055555586ba15 in _start ()
*/
/* This should not be used directly. Use block_init etc. instead. */
void register_module_init(void (*fn)(void), module_init_type type)
{
ModuleEntry *e;
ModuleTypeList *l;
e = g_malloc0(sizeof(*e));
e->init = fn;
e->type = type;
l = find_type(type);
QTAILQ_INSERT_TAIL(l, e, node);
}
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(PCIDevice),
.abstract = true,
.class_size = sizeof(PCIDeviceClass),
.class_init = pci_device_class_init,
.class_base_init = pci_device_class_base_init,
};
static void pci_register_types(void)
{
...
type_register_static(&pci_device_type_info);
}
type_init(pci_register_types)可以看到,QOM通过
__attribute((constructor))
标记让do_qemu_init_X()
函数在main()
函数之前运行。而do_qemu_init_X()
函数是将用户自定义函数(这里是pci_register_types()
函数)插入到init_type_list[MODULE_INIT_QOM]
链表上生成struct TypeImpl
在
main()
中,init_type_list[MODULE_INIT_QOM]
链表上所有的之前插入的用户自定义函数都会在module_call_init()中执行,调用栈如下所示1
2
3
4
5
6
7
8
9
10
11/*
* #0 type_register_static (info=0x555556ea5540 <pci_device_type_info>) at ../../qemu/qom/object.c:195
* #1 0x0000555555a9b619 in pci_register_types () at ../../qemu/hw/pci/pci.c:2848
* #2 0x00005555560b0d54 in module_call_init (type=MODULE_INIT_QOM) at ../../qemu/util/module.c:109
* #3 0x0000555555bd3ce4 in qemu_init_subsystems () at ../../qemu/system/runstate.c:818
* #4 0x0000555555bdb08b in qemu_init (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/vl.c:2786
* #5 0x0000555555e9282d in main (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/main.c:47
* #6 0x00007ffff7829d90 in __libc_start_call_main (main=main@entry=0x555555e92809 <main>, argc=argc@entry=29, argv=argv@entry=0x7fffffffdc58) at ../sysdeps/nptl/libc_start_call_main.h:58
* #7 0x00007ffff7829e40 in __libc_start_main_impl (main=0x555555e92809 <main>, argc=29, argv=0x7fffffffdc58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc48) at ../csu/libc-start.c:392
* #8 0x000055555586ba15 in _start ()
*/用户自定义函数通过type_register_static()生成struct TypeImpl数据,并将其插入到一个全局GHashTable中,如下所示。随后可以通过类的名称调用type_table_lookup获取struct TypeImpl数据,struct TypeImpl数据包含了struct TypeInfo数据和Class结构体数据(此时还未初始化),也就是类的全部信息。
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73/*
* #0 type_register_internal (info=0x555556ea5540 <pci_device_type_info>) at ../../qemu/qom/object.c:176
* #1 0x0000555555e9df0a in type_register (info=0x555556ea5540 <pci_device_type_info>) at ../../qemu/qom/object.c:190
* #2 0x0000555555e9df30 in type_register_static (info=0x555556ea5540 <pci_device_type_info>) at ../../qemu/qom/object.c:195
* #3 0x0000555555a9b619 in pci_register_types () at ../../qemu/hw/pci/pci.c:2848
* #4 0x00005555560b0d54 in module_call_init (type=MODULE_INIT_QOM) at ../../qemu/util/module.c:109
* #5 0x0000555555bd3ce4 in qemu_init_subsystems () at ../../qemu/system/runstate.c:818
* #6 0x0000555555bdb08b in qemu_init (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/vl.c:2786
* #7 0x0000555555e9282d in main (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/main.c:47
* #8 0x00007ffff7829d90 in __libc_start_call_main (main=main@entry=0x555555e92809 <main>, argc=argc@entry=29, argv=argv@entry=0x7fffffffdc58) at ../sysdeps/nptl/libc_start_call_main.h:58
* #9 0x00007ffff7829e40 in __libc_start_main_impl (main=0x555555e92809 <main>, argc=29, argv=0x7fffffffdc58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc48) at ../csu/libc-start.c:392
* #10 0x000055555586ba15 in _start ()
*/
static TypeImpl *type_register_internal(const TypeInfo *info)
{
TypeImpl *ti;
if (!type_name_is_valid(info->name)) {
fprintf(stderr, "Registering '%s' with illegal type name\n", info->name);
abort();
}
ti = type_new(info);
type_table_add(ti);
return ti;
}
static void type_table_add(TypeImpl *ti)
{
assert(!enumerating_types);
g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
}
static GHashTable *type_table_get(void)
{
static GHashTable *type_table;
if (type_table == NULL) {
type_table = g_hash_table_new(g_str_hash, g_str_equal);
}
return type_table;
}
struct TypeImpl
{
const char *name;
size_t class_size;
size_t instance_size;
size_t instance_align;
void (*class_init)(ObjectClass *klass, void *data);
void (*class_base_init)(ObjectClass *klass, void *data);
void *class_data;
void (*instance_init)(Object *obj);
void (*instance_post_init)(Object *obj);
void (*instance_finalize)(Object *obj);
bool abstract;
const char *parent;
TypeImpl *parent_type;
ObjectClass *class;
int num_interfaces;
InterfaceImpl interfaces[MAX_INTERFACES];
};初始化Class结构体
这里初始化类的最后一部分数据,即Class结构体。其往往在生成struct TypeImpl之后且对象初始化之前通过type_initialize()进行,如下所示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
38
39
40
41
42
43
44
45
46
47
48/*
* #0 pci_device_class_init (klass=0x555557108080, data=0x0) at ../../qemu/hw/pci/pci.c:2630
* #1 0x0000555555e9e904 in type_initialize (ti=0x5555570997c0) at ../../qemu/qom/object.c:418
* #2 0x0000555555e9e65d in type_initialize (ti=0x555557091ba0) at ../../qemu/qom/object.c:366
* #3 0x0000555555e9e65d in type_initialize (ti=0x555557091f60) at ../../qemu/qom/object.c:366
* #4 0x0000555555ea02b7 in object_class_foreach_tramp (key=0x5555570920e0, value=0x555557091f60, opaque=0x7fffffffd8a0) at ../../qemu/qom/object.c:1133
* #5 0x00007ffff7b9d6b8 in g_hash_table_foreach () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
* #6 0x0000555555ea03a7 in object_class_foreach (fn=0x555555ea0532 <object_class_get_list_tramp>, implements_type=0x555556274512 "machine", include_abstract=false, opaque=0x7fffffffd8f0) at ../../qemu/qom/object.c:1155
* #7 0x0000555555ea05c0 in object_class_get_list (implements_type=0x555556274512 "machine", include_abstract=false) at ../../qemu/qom/object.c:1212
* #8 0x0000555555bd8192 in select_machine (qdict=0x5555570e5ce0, errp=0x55555705bca0 <error_fatal>) at ../../qemu/system/vl.c:1661
* #9 0x0000555555bd935b in qemu_create_machine (qdict=0x5555570e5ce0) at ../../qemu/system/vl.c:2101
* #10 0x0000555555bdd50f in qemu_init (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/vl.c:3664
* #11 0x0000555555e9282d in main (argc=29, argv=0x7fffffffdc58) at ../../qemu/system/main.c:47
* #12 0x00007ffff7829d90 in __libc_start_call_main (main=main@entry=0x555555e92809 <main>, argc=argc@entry=29, argv=argv@entry=0x7fffffffdc58) at ../sysdeps/nptl/libc_start_call_main.h:58
* #13 0x00007ffff7829e40 in __libc_start_main_impl (main=0x555555e92809 <main>, argc=29, argv=0x7fffffffdc58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc48) at ../csu/libc-start.c:392
* #14 0x000055555586ba15 in _start ()
*/
static void type_initialize(TypeImpl *ti)
{
TypeImpl *parent;
if (ti->class) {
return;
}
...
ti->class = g_malloc0(ti->class_size);
parent = type_get_parent(ti);
if (parent) {
type_initialize(parent);
memcpy(ti->class, parent->class, parent->class_size);
...
}
ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
object_property_free);
ti->class->type = ti;
while (parent) {
if (parent->class_base_init) {
parent->class_base_init(ti->class, ti->class_data);
}
parent = type_get_parent(parent);
}
if (ti->class_init) {
ti->class_init(ti->class, ti->class_data);
}
}type_initialize()首先填充struct TypeImpl和Class结构体相关的字段,此时struct TypeImpl才完整的包含了类的所有信息。之后初始化所有父类的Class结构体,并依次调用所有父类的class_base_init()和自己的class_init()从而最终完成类的初始化。
对象初始化
根据前面章节的介绍,QOM使用Object结构体来描述对象,则对象的初始化也就是该数据结构的初始化。
QOM根据对象的类名称调用type_table_lookup()获取类对应的struct TypeImpl,然后使用object_initialize_with_type()来创建并初始化一个对象实例,如下所示
1 | /* |
object_initialize_with_type()首先调用type_initialize()确保类被初始化,然后调用object_init_with_type()和objet_post_init_with_type(),从而递归调用对象和对象所有父类的对象初始化相关函数。
类型转换
QOM同样实现了面向对象编程中的cast概念。根据类和对象章节的介绍,相关结构体在起始偏移处存放了父类的结构体,因此向上转型始终是安全的;而为了实现向下转型,QOM通过OBJECT_DECLARE_TYPE()宏声明了诸多的helper函数,如下所示
1 | /** |
可以看到,QOM提供了OBJ_NAME()将任何一个struct Object转换为Object结构体、OBJ_NAME##_GET_CLASS()从struct Object提取Class结构体和OBJ_NAME##_CLASS从struct ObjectClass转换为Class结构体的函数。
由于struct Object的class字段指向对应的struct ObjectClass,而struct ObjectClass的type字段指向真实的struct TypeImpl内容,基于此,QOM通过object_dynamic_cast_assert()和object_class_dynamic_cast_assert(),检查目标类或对象是否为这些指针的祖先从而进行安全的转换,如下所示
1 | Object *object_dynamic_cast_assert(Object *obj, const char *typename, |
属性
类似于linux中的sysfs,考虑到QOM的每个类的Class结构体基类是struct ObjectClass,每个对象的Object结构体基类是Object,为了提供一套类和对象的公用对外接口,QOM为struct ObjectClass和struct Object添加了properties域,即属性名称到struct ObjectProperty的哈希表,如下所示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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60struct ObjectProperty
{
char *name;
char *type;
char *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
ObjectPropertyInit *init;
void *opaque;
QObject *defval;
};
/**
* typedef ObjectPropertyAccessor:
* @obj: the object that owns the property
* @v: the visitor that contains the property data
* @name: the name of the property
* @opaque: the object property opaque
* @errp: a pointer to an Error that is filled if getting/setting fails.
*
* Called when trying to get/set a property.
*/
typedef void (ObjectPropertyAccessor)(Object *obj,
Visitor *v,
const char *name,
void *opaque,
Error **errp);
/**
* typedef ObjectPropertyResolve:
* @obj: the object that owns the property
* @opaque: the opaque registered with the property
* @part: the name of the property
*
* Resolves the #Object corresponding to property @part.
*
* The returned object can also be used as a starting point
* to resolve a relative path starting with "@part".
*
* Returns: If @path is the path that led to @obj, the function
* returns the #Object corresponding to "@path/@part".
* If "@path/@part" is not a valid object path, it returns #NULL.
*/
typedef Object *(ObjectPropertyResolve)(Object *obj,
void *opaque,
const char *part);
/**
* typedef ObjectPropertyRelease:
* @obj: the object that owns the property
* @name: the name of the property
* @opaque: the opaque registered with the property
*
* Called when a property is removed from a object.
*/
typedef void (ObjectPropertyRelease)(Object *obj,
const char *name,
void *opaque);
其中,name
表示属性的名称,type
表示属性的类型,而opaque
指向一个属性的具体类型的结构体信息,如下图所示1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18┌────────────────┐
│ │
│ │
├────────────────┤
│ properties ├───────────────┬──────────────────────────────────────►
├────────────────┤ │
│ │ │
│ │ ┌──────▼──────┐
└────────────────┘ │ name │
struct ObjectClass ├─────────────┤
│ type ├─────────►"bool"
├─────────────┤
│ ... │
├─────────────┤
│ opaque ├─────────►┌──────────┐
└─────────────┘ │ │
struct ObjectProperty └──────────┘
struct BoolProperty
QOM分别使用object_class_property_find()、object_class_property_add()和object_property_find()、object_property_add()来查找和设置这些属性,如下所示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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name)
{
ObjectClass *parent_klass;
parent_klass = object_class_get_parent(klass);
if (parent_klass) {
ObjectProperty *prop =
object_class_property_find(parent_klass, name);
if (prop) {
return prop;
}
}
return g_hash_table_lookup(klass->properties, name);
}
ObjectProperty *
object_class_property_add(ObjectClass *klass,
const char *name,
const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
ObjectPropertyRelease *release,
void *opaque)
{
ObjectProperty *prop;
assert(!object_class_property_find(klass, name));
prop = g_malloc0(sizeof(*prop));
prop->name = g_strdup(name);
prop->type = g_strdup(type);
prop->get = get;
prop->set = set;
prop->release = release;
prop->opaque = opaque;
g_hash_table_insert(klass->properties, prop->name, prop);
return prop;
}
ObjectProperty *object_property_find(Object *obj, const char *name)
{
ObjectProperty *prop;
ObjectClass *klass = object_get_class(obj);
prop = object_class_property_find(klass, name);
if (prop) {
return prop;
}
return g_hash_table_lookup(obj->properties, name);
}
bool object_property_set(Object *obj, const char *name, Visitor *v,
Error **errp)
{
ERRP_GUARD();
ObjectProperty *prop = object_property_find_err(obj, name, errp);
if (prop == NULL) {
return false;
}
if (!prop->set) {
error_setg(errp, "Property '%s.%s' is not writable",
object_get_typename(obj), name);
return false;
}
prop->set(obj, v, name, prop->opaque, errp);
return !*errp;
}
ObjectProperty *
object_property_add(Object *obj, const char *name, const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
ObjectPropertyRelease *release,
void *opaque)
{
return object_property_try_add(obj, name, type, get, set, release,
opaque, &error_abort);
}
ObjectProperty *
object_property_try_add(Object *obj, const char *name, const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
ObjectPropertyRelease *release,
void *opaque, Error **errp)
{
ObjectProperty *prop;
...
if (object_property_find(obj, name) != NULL) {
error_setg(errp, "attempt to add duplicate property '%s' to object (type '%s')",
name, object_get_typename(obj));
return NULL;
}
prop = g_malloc0(sizeof(*prop));
prop->name = g_strdup(name);
prop->type = g_strdup(type);
prop->get = get;
prop->set = set;
prop->release = release;
prop->opaque = opaque;
g_hash_table_insert(obj->properties, prop->name, prop);
return prop;
}