接前一篇文章:QEMU源码全解析18 —— QOM介绍(7)
本文内容参考:
《趣谈Linux操作系统》 —— 刘超,极客时间
《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一回讲到了QEMU的QOM中类型的层次结构,本文接着讲,把类型的层次结构讲完。
上文讲过,edu即TYPE_PCI_EDU_DEVICE类型的父类型是TYPE_PCI_DEVICE。当然,除了TYPE_PCI_DEVICE外,QEMU中还有其它类型,如TYPE_ISA_DEVICE,同样是以TYPE_DEVICE为父类型,表示的是ISA设备。仍然以《封神榜》做类比,这就相当于元始天尊徒弟不是只有玉鼎真人,还有哪吒的师傅太乙真人以及书胆姜子牙等。同理,TYPE_PCI_DEVICE也可以派生出其它的(子)类型(类比就是玉鼎真人除了杨戬外,也可以收其他徒弟)。总体上,QEMU使用的(所有)类型一起构成了以TYPE_OBJECT为根的树。
下边再从数据结构角度看一下类型的层次结构。
为了便于理解,再次贴出type_initialize函数中的相应代码片段(qom/object.c中):
static void type_initialize(TypeImpl *ti)
{
TypeImpl *parent;
if (ti->class) {
return;
}
ti->class_size = type_class_get_size(ti);
ti->instance_size = type_object_get_size(ti);
/* Any type with zero instance_size is implicitly abstract.
* This means interface types are all abstract.
*/
if (ti->instance_size == 0) {
ti->abstract = true;
}
if (type_is_ancestor(ti, type_interface)) {
assert(ti->instance_size == 0);
assert(ti->abstract);
assert(!ti->instance_init);
assert(!ti->instance_post_init);
assert(!ti->instance_finalize);
assert(!ti->num_interfaces);
}
ti->class = g_malloc0(ti->class_size);
……
}
在类型的初始化函数type_initialize中会调用ti->class = g_malloc0(ti->class_size)语句来分配类型的class结构,此结构实际上代表了类型的信息,类似于C++、Java等面向对象语言中定义的一个类。从前文的分析看到,ti->class_size为TypeImpl中的值,如果类型本身没有定义,就会使用父类型的class_size进行初始化。
此处再贴出edu设备的定义以便于理解下边内容(hw/misc/edu.c):
static void pci_edu_register_types(void)
{
static InterfaceInfo interfaces[] = {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
};
static const TypeInfo edu_info = {
.name = TYPE_PCI_EDU_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(EduState),
.instance_init = edu_instance_init,
.class_init = edu_class_init,
.interfaces = interfaces,
};
type_register_static(&edu_info);
}
type_init(pci_edu_register_types)
对于edu设备来说,其类型本身没有定义,因此它的class_size为其父类型TYPE_PCI_DEVICE中定义的值,即pci_device_type_info中的class_size的值:sizeof(PCIDeviceClass)。
PCIDeviceClass结构在include/hw/pci/pci.h中定义,代码如下:
struct PCIDeviceClass {
DeviceClass parent_class;
void (*realize)(PCIDevice *dev, Error **errp);
PCIUnregisterFunc *exit;
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;
uint16_t vendor_id;
uint16_t device_id;
uint8_t revision;
uint16_t class_id;
uint16_t subsystem_vendor_id; /* only for header type = 0 */
uint16_t subsystem_id; /* only for header type = 0 */
/*
* pci-to-pci bridge or normal device.
* This doesn't mean pci host switch.
* When card bus bridge is supported, this would be enhanced.
*/
bool is_bridge;
/* rom bar */
const char *romfile;
};
PCIDeviceClass表明了类属PCI设备的信息,如:表示设备制造商信息的vendor_id、表示设备信息的device_id以及读取PCI设备配置空间的config_read和config_write函数。
值得注意的是,PCIDeviceClass结构的第一个成员的类型DeviceClass结构,该结构描述的是属于“设备类型”的类型所具有的一些属性。在hw/core/qdev.c的device_type_info中可以看到:
static const TypeInfo device_type_info = {
.name = TYPE_DEVICE,
.parent = TYPE_OBJECT,
.instance_size = sizeof(DeviceState),
.instance_init = device_initfn,
.instance_post_init = device_post_init,
.instance_finalize = device_finalize,
.class_base_init = device_class_base_init,
.class_init = device_class_init,
.abstract = true,
.class_size = sizeof(DeviceClass),
.interfaces = (InterfaceInfo[]) {
{ TYPE_VMSTATE_IF },
{ TYPE_RESETTABLE_INTERFACE },
{ }
}
};
其中DeviceClass的定义在include/hw/qdev-core.h中,代码如下:
struct DeviceClass {
/*< private >*/
ObjectClass parent_class;
/*< public >*/
DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX);
const char *fw_name;
const char *desc;
/*
* The underscore at the end ensures a compile-time error if someone
* assigns to dc->props instead of using device_class_set_props.
*/
Property *props_;
/*
* Can this device be instantiated with -device / device_add?
* All devices should support instantiation with device_add, and
* this flag should not exist. But we're not there, yet. Some
* devices fail to instantiate with cryptic error messages.
* Others instantiate, but don't work. Exposing users to such
* behavior would be cruel; clearing this flag will protect them.
* It should never be cleared without a comment explaining why it
* is cleared.
* TODO remove once we're there
*/
bool user_creatable;
bool hotpluggable;
/* callbacks */
/*
* Reset method here is deprecated and replaced by methods in the
* resettable class interface to implement a multi-phase reset.
* TODO: remove once every reset callback is unused
*/
DeviceReset reset;
DeviceRealize realize;
DeviceUnrealize unrealize;
/* device state */
const VMStateDescription *vmsd;
/* Private to qdev / bus. */
const char *bus_type;
};
DeviceClass定义了设备类型相关的基本信息以及基本的回调函数。仍然注意其中第一个域,表示的是DeviceClass父类型的Class —— ObjectClass。前文已提到,ObjectClass是所有类型的基础,会被嵌入到对应的子Class的第一个域中。
ObjectClass、DeviceClass和PCIDeviceClass三者的包含与被包含关系如下图所示:
事实上,编译器为C++继承结构编译出来的内存分布与此是类似的。
下一回将讲解父类型成员域的初始化以及其它相关代码。