qemu面向对象的模型:QOM
qemu用结构体实现了一套面向对象的机制,用于实现类和对象的概念,也实现了面向对象的封装、继承、多态。大部分设备模拟代码都会基于这个qom模型。
相关数据结构:
类定义:
struct ObjectClass
{
/*< private >*/
Type type; /* typedef struct TypeImpl *Type */
GSList *interfaces;
const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];
const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent *unparent;
GHashTable *properties;
};
对象定义:
struct Object
{
/*< private >*/
ObjectClass *class;
ObjectFree *free; /* 当引用技术为0时,清理垃圾的回调函数 */
GHashTable *properties; /* 属性的Hash表*/
uint32_t ref; /* 对象引用计数 */
Object *parent;
};
属性定义:
typedef struct ObjectProperty
{
gchar *name;
gchar *type;
gchar *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
void *opaque;
} ObjectProperty;
每个object对象会有上述对应的属性结构,并用哈希表组织了起来。当需要增加或者删除属性时,可调用object_property_add或object_property_del函数。
定义一个类型:
struct TypeInfo
{
const char *name;
const char *parent;
size_t instance_size;
void (*instance_init)(Object *obj); /* 实例初始化 */
void (*instance_post_init)(Object *obj);
void (*instance_finalize)(Object *obj); /* 实例析构 */
bool abstract;
size_t class_size;
void (*class_init)(ObjectClass *klass, void *data); /* 类初始化 */
void (*class_base_init)(ObjectClass *klass, void *data);
void (*class_finalize)(ObjectClass *klass, void *data); /* 类析构 */
void *class_data;
InterfaceInfo *interfaces;
};
#define type_init(function) module_init(function, MODULE_INIT_QOM)
type_register函数会根据TypeInfo结构体生成TypeImpl结构体,并将name作为key,创建全局hash表:
struct TypeImpl
{
const char *name;
size_t class_size;
size_t instance_size;
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];
};
类的基本信息是通过这个TypeImpl体现的
类的数据结构一般包含大量的函数指针,用于对对象进行操作。class对应的init函数可以将这些函数指针初始化。然后所有含有这个类数据结构指针的对象,都可以调用这些函数。 — 共性东西拿出来搞成Class,泛化
对象的数据结构一般包含了大量变量,是对象的一些属性,很明显这些是每个实例特有的,各自不尽相同。instance对应的init函数可以把这些属性初始化,相当于C++中的构造函数。 — 特化的东西构成实例
初始化相关:
已virtio-net-pci为例:
-
type_init(virtio_net_pci_register) -> register_module_init : 会将virtio_net_pci_register放到全局二维数组中init_type_list,数组成员为ModuleEntry类型,内部有init函数指针,virtio_net_pci_register会赋值给init
-
main() -> module_call_init(MODULE_INIT_QOM) : 会遍历init_type_list[MODULE_INIT_QOM]中的所有元素,并调用init]
-> virtio_net_pci_register -> type_register -> type_register_internal -> type_table_add [通过该调用链,对应TypeImpl结构被加入到全局hash表中 -
main -> select_machine -> object_class_get_list -> object_class_foreach ->object_class_get_list_tramp -> type_initialize
static void type_initialize(TypeImpl *ti) { .... if (ti->class) { /* 非空,已经初始化过,直接退出 */ return; } ... if (parent) { type_initialize(parent); /* 递归调用,回溯到根节点 */ ... memcpy(ti->class,parent->class,parent->class_size);/* 复制父节点的class结构体,继承的实现本质 */ ... } ... if (ti->class_init) { ti->class_init(ti->class, ti->class_data); /* 类初始化函数 */ } }
【TypeImpl是一种辅助数据结构,类的创建以及初始化会基于该结构体】
-
main -> qemu_opts_foreach -> device_init_func -> device_init_func -> qdev_device_add -> object_new : 该调用链会遍历全局保存的QemuOptsList数组(vm_config_groups),找到类型为device的单元,例如对应下面的命令行选项:
-netdev tap,id=hostnet0,ifname=vnet2,downscript=no -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:6b:0d:a1,bus=pci.0,addr=0x3
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) { DeviceClass *dc; const char *driver, *path; DeviceState *dev; BusState *bus = NULL; ... driver = qemu_opt_get(opts, "driver"); /* 此处对应virtio-net-pci */ ... dc = qdev_get_device_class(&driver, errp); ... path = qemu_opt_get(opts, "bus"); /* 寻找对应总线路径,此处对应pci.0 */ if (path != NULL) { bus = qbus_find(path, errp); /* 根据path寻找对应的bus实例 */ if (!bus) { return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { error_setg(errp, "Device '%s' can't go on %s bus", driver, object_get_typename(OBJECT(bus))); return NULL; } /* 总线不匹配 */ } ... dev = DEVICE(object_new(driver)); /* 创建设备实例 */ if (bus) { qdev_set_parent_bus(dev, bus); /* 设备与总线关联 */ } ... if (qemu_opt_foreach(opts, set_property, dev, &err)) { goto err_del_dev; } /* 根据opts设置属性,上例会设置的属性有:netdev、mac、addr */ /* 对于netdev属性,后面会调用set_netdev函数,使得device和netdev发生关联,前面已经具体介绍过 */ dev->opts = opts; object_property_set_bool(OBJECT(dev), true, "realized", &err); /* 触发realize回调 */ }
object_new -> object_new_with_type -> object_initialize_with_type -> object_initialize_with_type -> object_init_with_type
static void object_init_with_type(Object *obj, TypeImpl *ti) { if (type_has_parent(ti)) { object_init_with_type(obj, type_get_parent(ti));/* 回溯到根节点,递归调用 */ } if (ti->instance_init) { ti->instance_init(obj); /* 实例初始化函数 */ } }
-
另外关注一下DeviceClass这个结构体,其中有个realize回调,设备的许多初始化操作是在这个函数中进行的。
typedef struct DeviceClass { /*< private >*/ ObjectClass parent_class; /*< public >*/ DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); const char *fw_name; const char *desc; Property *props; bool user_creatable; bool hotpluggable; /* callbacks */ DeviceReset reset; DeviceRealize realize; /* 更具体的初始化操作 */ DeviceUnrealize unrealize; /* device state */ const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ const char *bus_type; };
以virtio_net_device_realize为例:
main -> qemu_opts_foreach -> device_init_func -> qdev_device_add -> object_property_set_bool -> property_set_bool -> device_set_realized -> pci_qdev_realize -> virtio_pci_realize -> object_property_set_qobject -> property_set_bool -> device_set_realized -> virtio_net_device_realize
realize这个属性是在object_init_with_type回溯到DeciceClass节点调用对应的instance_init实现的,对应的函数为:
static void device_initfn(Object *obj)
{
....
object_property_add_bool(obj, "realized",
device_get_realized, device_set_realized, NULL);
....
}