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

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

本文内容参考:

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

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。

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/131934977