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为例:

  1. type_init(virtio_net_pci_register) -> register_module_init : 会将virtio_net_pci_register放到全局二维数组中init_type_list,数组成员为ModuleEntry类型,内部有init函数指针,virtio_net_pci_register会赋值给init
  2. 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表中
  3. 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是一种辅助数据结构,类的创建以及初始化会基于该结构体】

  1. 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); /* 实例初始化函数 */
    }
}
  1. 另外关注一下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);
	....
}