接前一篇文章: 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的强制转换了。
至此,类型的层次结构就讲解完了。