QEMU源码全解析11 —— 定义一个QEMU模块(3)

88 篇文章 19 订阅
本文深入解析QEMU模块的type_register_internal函数,探讨type_new、type_table_add及其作用。通过分析QEMU的TypeImpl结构,解释其在面向对象编程中的角色,以及如何避免重复创建。同时,文章引入QOM(QEMU Object Model)作为QEMU的反射机制,并预告了后续关于QOM的内容。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析10 —— 定义一个QEMU模块(2)

本文内容参考:

《趣谈 Linux操作系统 》 —— 刘超, 极客时间

QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社

特此致谢!

上一回讲到了type_register_internal函数,在qom/object.c中,再次贴出代码:

  1. static TypeImpl *type_register_internal(const TypeInfo *info)
  2. {
  3. TypeImpl *ti;
  4. ti = type_new(info);
  5. type_table_add(ti);
  6. return ti;
  7. }

其中共调用了2个函数:type_new和type_table_add。本文对于这2个函数以及type_register_internal函数整体功能进行深入解析。

  • type_new(info)

type_new函数又是在type_register_internal函数的上边,代码如下:

  1. static TypeImpl *type_new(const TypeInfo *info)
  2. {
  3. TypeImpl *ti = g_malloc0(sizeof(*ti));
  4. int i;
  5. g_assert(info->name != NULL);
  6. if (type_table_lookup(info->name) != NULL) {
  7. fprintf(stderr, "Registering `%s' which already exists\n", info->name);
  8. abort();
  9. }
  10. ti->name = g_strdup(info->name);
  11. ti->parent = g_strdup(info->parent);
  12. ti->class_size = info->class_size;
  13. ti->instance_size = info->instance_size;
  14. ti->instance_align = info->instance_align;
  15. ti->class_init = info->class_init;
  16. ti->class_base_init = info->class_base_init;
  17. ti->class_data = info->class_data;
  18. ti->instance_init = info->instance_init;
  19. ti->instance_post_init = info->instance_post_init;
  20. ti->instance_finalize = info->instance_finalize;
  21. ti->abstract = info->abstract;
  22. for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
  23. ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
  24. }
  25. ti->num_interfaces = i;
  26. return ti;
  27. }

对于kvm_type_init函数中调用的type_register_static(&kvm_accel_type),最终传入到type_new函数中的参数是&kvm_accel_type。

为了便于理解,再次贴出kvm_accel_type的初始化代码:

  1. static const TypeInfo kvm_accel_type = {
  2. .name = TYPE_KVM_ACCEL,
  3. .parent = TYPE_ACCEL,
  4. .instance_init = kvm_accel_instance_init,
  5. .class_init = kvm_accel_class_init,
  6. .instance_size = sizeof(KVMState),
  7. };

实际上,type_new函数是利用TypeInfo结构中的信息创建并初始化一个TypeImpl结构实例。

TypeImpl结构在qom/object.c中定义,代码如下:

  1. typedef struct InterfaceImpl InterfaceImpl;
  2. typedef struct TypeImpl TypeImpl;
  3. struct InterfaceImpl
  4. {
  5. const char *typename;
  6. };
  7. struct TypeImpl
  8. {
  9. const char *name;
  10. size_t class_size;
  11. size_t instance_size;
  12. size_t instance_align;
  13. void (*class_init)(ObjectClass *klass, void *data);
  14. void (*class_base_init)(ObjectClass *klass, void *data);
  15. void *class_data;
  16. void (*instance_init)(Object *obj);
  17. void (*instance_post_init)(Object *obj);
  18. void (*instance_finalize)(Object *obj);
  19. bool abstract;
  20. const char *parent;
  21. TypeImpl *parent_type;
  22. ObjectClass *class;
  23. int num_interfaces;
  24. InterfaceImpl interfaces[MAX_INTERFACES];
  25. };

每一个Module既然要模拟某种设备,那么应该定义一种类型TypeImpl来表示这种设备,这其实是一种 面向对象编程 的思想,只不过这里用的是C语言的实现,所以需要变相地实现一下类和对象。

这里顺带说明一下,type_new函数中在创建TypeImpl结构实例之前,先调用了type_table_lookup函数以查看系统中是否已经创建(注册)过了该实例,如果已经存在即创建过了,则不再重复创建。type_table_lookup函数在qom/object.c中,代码如下:

  1. static TypeImpl *type_table_lookup(const char *name)
  2. {
  3. return g_hash_table_lookup(type_table_get(), name);
  4. }
  • type_table_add(ti)

type_table_add函数同样在qom/object.c中,代码如下:

  1. static void type_table_add(TypeImpl *ti)
  2. {
  3. assert(!enumerating_types);
  4. g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
  5. }

type_table_get函数就在上边,代码如下:

  1. static GHashTable *type_table_get(void)
  2. {
  3. static GHashTable *type_table;
  4. if (type_table == NULL) {
  5. type_table = g_hash_table_new(g_str_hash, g_str_equal);
  6. }
  7. return type_table;
  8. }

而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文件夹下面。如下所示:

  1. ~/QEMU_kvm/QEMU/source_codes/qemu-7.1.0$ ls qom/
  2. 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。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他