QOM模型初始化流程

Qemu Object Model

    QOM (Qemu Object Model)是Qemu实现的面向对象编程模式。Qemu是用C语言编写的,而C语言是面向过程的编程语言,无法享受面向对象编程模式针对复杂软件系统在设计模式上的优越性。为解决该问题,Qemu社区通过C语言实现了一套面向对象的编程接口,即QOM,并成功将其应用在Qemu设备模型的管理中。

1、简介

QOM是一套创建“类”和“对象”的编程接口。这套接口也是一种支持用户创建类型并根据类型实例化对象的灵活框架,其具有以下特性:

  • 支持动态注册并创建类型;
  • 支持类型的单继承(仅有一个父类);
  • 支持类型的多继承;

2、 一个简单应用实例

下面通过一个简单代码实例,先来了解QOM的基本使用方法。

#include "qdev.h"

#define TYPE_MY_DEVICE "my-device"

 
// No new virtual functions: we can reuse the typedef for the

// superclass.

typedef DeviceClass MyDeviceClass;

typedef struct MyDevice

{

     DeviceState parent;

 

     int reg0, reg1, reg2;

} MyDevice;

 

static const TypeInfo  my_device_info = {

     .name = TYPE_MY_DEVICE,

     .parent = TYPE_DEVICE,

     .instance_size = sizeof(MyDevice),

};
static void my_device_register_types(void) { type_register_static(&my_device_info); }    type_init(my_device_register_types)

 

这个代码示例的功能是通过QOM接口(type_init, type_register_static)向QOM系统注册了一个TYPE_MY_DEVICE类型。该类型包含一个class类(结构) MyDeviceClass和一个object类(结构)MyDevice。class类包括一组函数指针,用来灵活指明调用函数;object类通常用来表达对象的属性信息。一个class类仅会实例化出一个class对象,而object类可以实例化出多个对象且每个对象都指向同一个class对象,如图1所示。其中ObjectClass和Object为QOM定义的基本结构,DeviceClass和DeviceState是Qemu为设备模型定义的基本结构。

                              

 

 

 

图1. 示例类与对象关系图

从这个示例中还可以看出类型TYPE_MY_DEVICE的父类型是TYPE_DEVICE (见my_device_info中的parent字段),但是实际的父子关系是在具体的类型定义中体现的,如MyDevice中的第一个字段parent即是DeviceState类,MyDeviceClass和DeviceClass为同一结构。

3、QOM接口基本用法

总的来说,QOM提供的接口可分为五类:类型注册、对象创建、对象获取、属性操作和类型转换。

3.1 、类型注册

TypeImpl *type_register_static(const TypeInfo *info);

该接口向QOM系统注册了一个由TypeInfo描述的类型。

 

struct TypeInfo{
    const char *name;  //表示该类型的字符串名称;

    const char *parent;  //表示该类型的父类型的字符串名称;

    size_t instance_size;  //该类型所能创建的object类对象的内存空间占用大小;如果该值为0,则使用父类的instance_size;

    void (*instance_init)(Object *obj);//创建的object类对象的初始化函数;仅初始化该类型自身的成员属性,不包含父类型成员(通常父类型已经初始化完成);

    void (*instance_finalize)(Object *obj);//创建的object类对象的析构函数;

    bool abstract;//表示该类型是否为抽象类;抽象类不能实例化object类对象;

    size_t class_size;//该类型所能创建的class类对象的内存空间占用大小;如果该值为0,则使用父类型的class_size;

    void (*class_init)(ObjectClass *klass, void *data);//创建的class类对象的初始化函数;仅初始化该类型自身的函数指针,不包含父类;

    void (*class_base_init)(ObjectClass *klass, void *data);//该函数在父类型的class类对象初始化完成后,在当前class类对象的class_init执行前被调用;

    void (*class_finalize)(ObjectClass *klass, void *data);//创建的class类对象的析构函数;

    void *class_data;//传递给class_init、class_base_init和class_finalize的参数;

    InterfaceInfo *interfaces;//该类型的接口列表;

};

1.2       对象创建

Object *object_new(const char *typename);

该接口创建一个类型名为typename的object类对象。创建过程中,如果该类型对应的class类对象不存在,还会创建该class类对象并调用其class_init初始化;如果存在父类型,还会递归创建父类的class类对象并初始化。
Class类对象创建完成后,还会对当前object类对象执行父类和自身的instance_init函数。最终,对象初始化完成后,其引用计数为1。
 
Object *object_new_with_type(Type type);

该接口功能同object_new,参数由字符名改为Type。
void object_initialize(void *obj, const char *typename);
该接口对已经存在的对象obj,按typename类型进行初始化操作。
Object *object_dynamic_cast(Object *obj, const char *typename);
该接口将已经存在的对象obj强转成typename类型。

1.3       对象获取

ObjectClass *object_get_class(Object *obj);
该接口获取obj对象的唯一class类对象。
const char *object_get_typename(Object *obj);
该接口获取obj对象的字符串类型名。
ObjectClass *object_class_get_parent(ObjectClass *klass);
该接口获取class对象klass的父类的class对象。
const char *object_class_get_name(ObjectClass *klass);
该接口获取class对象klass对应的类型的字符串名称

1.4       属性操作

void object_property_add(Object *obj, const char *name, const char *type,

                         ObjectPropertyAccessor *get,

                         ObjectPropertyAccessor *set,

                         ObjectPropertyRelease *release,

                         void *opaque, struct Error **errp);

void object_property_del(Object *obj, const char *name, struct Error **errp);

ObjectProperty *object_property_find(Object *obj, const char *name,

                                     struct Error **errp);

void object_property_get(Object *obj, struct Visitor *v, const char *name,

                         struct Error **errp);

void object_property_set(Object *obj, struct Visitor *v, const char *name,

                         struct Error **errp);

void object_property_set_str(Object *obj, const char *value,

                             const char *name, struct Error **errp);

char *object_property_get_str(Object *obj, const char *name,

                              struct Error **errp);

void object_property_set_link(Object *obj, Object *value,

                              const char *name, struct Error **errp);

Object *object_property_get_link(Object *obj, const char *name,

                                 struct Error **errp);

void object_property_set_bool(Object *obj, bool value,

                              const char *name, struct Error **errp);

bool object_property_get_bool(Object *obj, const char *name,

                              struct Error **errp);

void object_property_set_int(Object *obj, int64_t value,

                             const char *name, struct Error **errp);

int64_t object_property_get_int(Object *obj, const char *name,

                                struct Error **errp);

void object_property_add_child(Object *obj, const char *name,

                               Object *child, struct Error **errp);

void object_property_add_link(Object *obj, const char *name,

                              const char *type, Object **child,

                              struct Error **errp);

1.5       类型转换

#define OBJECT_CHECK(type, obj, name) \

    ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \

                                        __FILE__, __LINE__, __func__))

该宏将obj强转成type类型。
#define OBJECT_CLASS_CHECK(class, obj, name) \

    ((class *)object_class_dynamic_cast_assert(OBJECT_CLASS(obj), (name), \

                                               __FILE__, __LINE__, __func__))

该宏将obj强转成对应的class类对象。

4、Qemu设备模型

 

5、QOM设备初始化调用流程(virtio-balloon为例)

 

6. 数据结构组成:

 7. QEMU侧QOM模型结构

 8. 一个自建设备的初始化流程

1. 存储init执行函数  
在main_impl执行之前,通过type_init将设备init执行函数存入全局数组init_type_list\[QOM\],调用关系如下,参考流程图①:  
```c
register_module_init(mydevice_register_types)
->find_type(init_type_list[QOM])->INSERT
```

2. 存储TypeInfo  
main_impl中执行**module_call_init**, 遍历**init_type_list**中注册的QOM类型设备的执行函数,将TypeInfo转换为TypeImpl存储到全局hash表**type_table**。调用关系如下,参考流程图②:
```c
module_call_init(QOM) -> mydevice_register_types -> type_register_static -> type_table_add(TypeInfo->TypeImpl) -> g_hash_table_insert(type_table)
```

3. class初始化  
在查找主板时,提前对所有设备的class进行了初始化,调用关系如下,参考流程图③:
```c
select_machine -> find_default_machine -> object_class_foreach(type_table) -> object_class_foreach_tramp -> type_initialize(ti) -> ti->class_init -> mydevice_class_init
```

4. instance初始化  
main_impl中执行设备初始化时,会遍历所有添加到命令参数-device中的设备,新设备在此将完成实例化创建。调用关系如下,参考流程图④:
```c
qemu_opts_foreach(device) -> device_init_func -> qdev_device_add -> object_new(my-device) -> object_new_with_type(ti) -> object_initialize_with_type(ti) -> ti->instance_init -> mydevice_instance_init
```

5. 设备实例化
当设备固有属性初始化完成后,将进行设备的创建操作,对应操作仍在**qdev_device_add**中,在**object_new**完成后执行。设备创建通过qdev的**realize**体系完成,在instance_init环节中qdev注册了realize属性,设备创建将通过realize属性置**true**来实现。调用关系如下,参考流程图⑤:
```c
qdev_device_add -> object_property_set_bool(realize) -> device_set_realized -> dc->realize -> mydevice_realize
```
此处需要注意,先前在class_init中修改了父设备的realize执行函数,因此此处dc->realize将指向我们自己定义的函数

 

posted @ 2021-04-21 10:23  Edver  阅读(1598)  评论(0编辑  收藏  举报