QEMU源码全解析20 —— QOM介绍(9)

本文深入探讨QEMU中的QOM(对象模型)父类型成员域初始化过程,从type_initialize函数入手,分析memcpy如何进行初始化,并详细解释了类的初始化过程中ObjectClass到PCIDeviceClass的转换,涉及object_class_dynamic_cast函数和type_is_ancestor的使用,揭示了类型层次结构的工作原理。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析19 —— QOM介绍(8)

本文内容参考:

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

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

特此致谢!

本回讲解父类型成员域的初始化相关内容。

父类型的成员域是在什么时候初始化的呢?还得从type_initialize函数说起。为了便于理解,再次贴出type_initialize函数的代码片段。在qom/object.c中,如下所示:

    ……
    ti->class = g_malloc0(ti->class_size);

    parent = type_get_parent(ti);
    if (parent) {
        type_initialize(parent);
        GSList *e;
        int i;

        g_assert(parent->class_size <= ti->class_size);
        g_assert(parent->instance_size <= ti->instance_size);
        memcpy(ti->class, parent->class, parent->class_size);
        ti->class->interfaces = NULL;
        ……
    ……

正是调用的memcpy(ti->class, parent->class, parent->class_size);这一行代码对父类型所占的这部分空间进行的初始化。

再来看分析类的初始化,type_initialize函数最后一段代码为:

    ……
    if (ti->class_init) {
        ti->class_init(ti->class, ti->class_data);
    }
}

第一个参数为ti->class,对于edu来说,就是刚刚分配的PCIDeviceClass。但是class_init回调的参数指定的类型是ObjectClass,所以还需要完成ObjectClass到PCIDeviceClass的转换。可以在hw/misc/edu.c的edu_class_init函数中看到这一转换,代码如下:

static void edu_class_init(ObjectClass *class, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(class);
    PCIDeviceClass *k = PCI_DEVICE_CLASS(class);

    k->realize = pci_edu_realize;
    k->exit = pci_edu_uninit;
    k->vendor_id = PCI_VENDOR_ID_QEMU;
    k->device_id = 0x11e8;
    k->revision = 0x10;
    k->class_id = PCI_CLASS_OTHERS;
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}

类型的转换是由PCI_DEVICE_CLASS完成的(当然DEVICE_CLASS也完成了转换)。该宏经过层层扩展,最终会调用到object_class_dynamic函数。该函数在qom/object.c中,代码如下:

ObjectClass *object_class_dynamic_cast(ObjectClass *class,
                                       const char *typename)
{
    ObjectClass *ret = NULL;
    TypeImpl *target_type;
    TypeImpl *type;

    if (!class) {
        return NULL;
    }

    /* A simple fast path that can trigger a lot for leaf classes.  */
    type = class->type;
    if (type->name == typename) {
        return class;
    }

    target_type = type_get_by_name(typename);
    if (!target_type) {
        /* target class type unknown, so fail the cast */
        return NULL;
    }

    if (type->class->interfaces &&
            type_is_ancestor(target_type, type_interface)) {
        int found = 0;
        GSList *i;

        for (i = class->interfaces; i; i = i->next) {
            ObjectClass *target_class = i->data;

            if (type_is_ancestor(target_class->type, target_type)) {
                ret = target_class;
                found++;
            }
         }

        /* The match was ambiguous, don't allow a cast */
        if (found > 1) {
            ret = NULL;
        }
    } else if (type_is_ancestor(type, target_type)) {
        ret = class;
    }

    return ret;
}

从函数名object_class_dynamic_cast就可以看出,这是一种动态转换,C++也有类似的dynamic_cast来完成从父类转换到子类的工作。object_class_dynamic_cast函数的第一个参数是需要转换的ObjectClass,第二个参数typename表示要转换到哪一种类型。

object_class_dynamic_cast函数首先通过type_get_by_name函数得到要转换到的TypeImpl,此处的typename是TYPE_PCI_DEVICE。代码片段如下:

    /* A simple fast path that can trigger a lot for leaf classes.  */
    type = class->type;
    if (type->name == typename) {
        return class;
    }

    target_type = type_get_by_name(typename);
    if (!target_type) {
        /* target class type unknown, so fail the cast */
        return NULL;
    }

以edu为例,type->name是"edu",但是要转换到的却是TYPE_PCI_DEVICE,即include/hw/pci/pci.h中定义的"pci-device"。代码如下:

#define TYPE_PCI_DEVICE "pci-device"

因此,会调用type_is_ancestor("edu", TYPE_PCI_DEVICE)来判断后者是否是前者的祖先。

在该函数中依次得到edu的父类型,然后判断是否与TYPE_PCI_DEVICE相等,由edu设备的TypeInfo可知其父类型为TYPE_PCI_DEVICE,所以这个type_is_ancestor会成功,能够进行从ObjectClass到PCIDeviceClass的转换。这样就可以直接通过(PCIDeviceClass *)ObjectClass完成从ObjectClass到PCIDeviceClass的强制转换了。

至此,类型的层次结构就讲解完了。