接前一篇文章: QEMU源码全解析10 —— 定义一个QEMU模块(2)
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一回讲到了type_register_internal函数,在qom/object.c中,再次贴出代码:
- static TypeImpl *type_register_internal(const TypeInfo *info)
- {
- TypeImpl *ti;
- ti = type_new(info);
-
- type_table_add(ti);
- return ti;
- }
其中共调用了2个函数:type_new和type_table_add。本文对于这2个函数以及type_register_internal函数整体功能进行深入解析。
- type_new(info)
type_new函数又是在type_register_internal函数的上边,代码如下:
- static TypeImpl *type_new(const TypeInfo *info)
- {
- TypeImpl *ti = g_malloc0(sizeof(*ti));
- int i;
-
- g_assert(info->name != NULL);
-
- if (type_table_lookup(info->name) != NULL) {
- fprintf(stderr, "Registering `%s' which already exists\n", info->name);
- abort();
- }
-
- ti->name = g_strdup(info->name);
- ti->parent = g_strdup(info->parent);
-
- ti->class_size = info->class_size;
- ti->instance_size = info->instance_size;
- ti->instance_align = info->instance_align;
-
- ti->class_init = info->class_init;
- ti->class_base_init = info->class_base_init;
- ti->class_data = info->class_data;
-
- ti->instance_init = info->instance_init;
- ti->instance_post_init = info->instance_post_init;
- ti->instance_finalize = info->instance_finalize;
-
- ti->abstract = info->abstract;
-
- for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
- ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
- }
- ti->num_interfaces = i;
-
- return ti;
- }
对于kvm_type_init函数中调用的type_register_static(&kvm_accel_type),最终传入到type_new函数中的参数是&kvm_accel_type。
为了便于理解,再次贴出kvm_accel_type的初始化代码:
- static const TypeInfo kvm_accel_type = {
- .name = TYPE_KVM_ACCEL,
- .parent = TYPE_ACCEL,
- .instance_init = kvm_accel_instance_init,
- .class_init = kvm_accel_class_init,
- .instance_size = sizeof(KVMState),
- };
实际上,type_new函数是利用TypeInfo结构中的信息创建并初始化一个TypeImpl结构实例。
TypeImpl结构在qom/object.c中定义,代码如下:
- typedef struct InterfaceImpl InterfaceImpl;
- typedef struct TypeImpl TypeImpl;
-
- struct InterfaceImpl
- {
- const char *typename;
- };
-
- 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];
- };
每一个Module既然要模拟某种设备,那么应该定义一种类型TypeImpl来表示这种设备,这其实是一种 面向对象编程 的思想,只不过这里用的是C语言的实现,所以需要变相地实现一下类和对象。
这里顺带说明一下,type_new函数中在创建TypeImpl结构实例之前,先调用了type_table_lookup函数以查看系统中是否已经创建(注册)过了该实例,如果已经存在即创建过了,则不再重复创建。type_table_lookup函数在qom/object.c中,代码如下:
- static TypeImpl *type_table_lookup(const char *name)
- {
- return g_hash_table_lookup(type_table_get(), name);
- }
- type_table_add(ti)
type_table_add函数同样在qom/object.c中,代码如下:
- static void type_table_add(TypeImpl *ti)
- {
- assert(!enumerating_types);
- g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
- }
type_table_get函数就在上边,代码如下:
- 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;
- }
而g_hash_table_new、g_hash_table_insert以及上边的g_hash_table_lookup函数,都是glib库中的函数,我们会开专门的文章进行详细介绍。
在分析完type_new和type_table_get函数之后,可以得出type_register_internal的整体功能:
在type_register_internal函数中,会根据传入的TypeInfo(本例中是kvm_accel_type),创建一个TypeImpl来代表新注册的类。也就是说,TypeImpl才是我们想要声明的那个class(类)。在 QEMU 中,有一个全局的哈希表type_table,用来存放所有已定义的类,更准确地说应该是已经注册过的类。在type_new函数中,先在此全局哈希表type_table中根据名字(TypeInfo结构中的name字段,本例中是kvm_accel_type的name成员的值TYPE_KVM_ACCEL)查找这个类。如果找到了,说明这个类曾经被创建(注册)过,报错退出,不往下进行;如果没有找到,说明这是一个新的类,则将TypeInfo里边的信息填到TypeImpl中。接下来,type_table_add函数会将这个类注册到全局表type_table中。
至此要注意,class_init(对于本例是kvm_accel_class_init)还没有被调用,即这个类到目前为止还处于纸面的状态。
以上机制有点类似于Java的反射机制。在Java中,对于一个类,首先写代码的时候要写一个class xxx的定义,编译好之后就放在.class文件中,也是处于纸面的状态。然后,Java会有一个Class对象,用于读取和表示这个纸面上的class xxx,生成真正的对象。
后文书讲到class_init时会看到,class_init中会生成XXXClass,就相当于Java中的Class对象,TypeImpl中还有一个instance_init函数(对于本例是kvm_accel_instance_init),相当于构造函数,用于根据XXXClass生成Object,这就相当于Java反射机制里最终创建的真正的对象。
这一套反射机制在QEMU源码中被放在了qom文件夹下面。如下所示:
- ~/QEMU_kvm/QEMU/source_codes/qemu-7.1.0$ ls qom/
- container.c meson.build object.c object_interfaces.c qom-hmp-cmds.c qom-qmp-cmds.c qom-qobject.c trace-events trace.h
QOM全称为QEMU Object Model,是用C语言实现了一套面向对象的反射机制。下一篇文章将详细介绍和讲解QOM。