写在前面
作为QEMU的入门,这篇文章主要是帮我理清楚QEMU中QOM部分内容的。本文主要分成两部分,一部分介绍QOM的主要数据结构和大致的实现,一部分是一些实例,这些实例实际上代码中注释的例子。
QOM主要数据结构的关系
这里借用一张现成的大佬画的图,借助这张图能够更好的理清QOM的各个数据结构。
TypeImpl
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];
};
这个数据结构存储类的基本信息,QEMU中存在一个全局的hash表,储存所有注册的该数据结构的指针。
利用TypeInfo这个结构,注册TypeImpl。TypeInfo结构实际上是和TypeImpl对应的。
struct TypeInfo
{
const char *name; // 这个类型的名字
const char *parent; //这个类型的父类的名字
size_t instance_size; //对象对应数据结构的size
// instance如何初始化和最后的垃圾回收
void (*instance_init)(Object *obj);
void (*instance_post_init)(Object *obj);
void (*instance_finalize)(Object *obj);
bool abstract; //这个类是否是抽象的,也就是是否有虚拟函数
size_t class_size; //类对应数据结构的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;
};
QOM提供type_register和type_register_static方法,调用这两个方法注册一个Type,需要传进去的参数就是TypeInfo的指针。
TypeImpl *type_register(const TypeInfo *info)
{
assert(info->parent);
return type_register_internal(info);
}
TypeImpl *type_register_static(const TypeInfo *info)
{
return type_register(info);
}
static TypeImpl *type_register_internal(const TypeInfo *info)
{
TypeImpl *ti;
ti = type_new(info);
type_table_add(ti);
return ti;
}
type_new函数实际就是利用info队impl进行填充。
Object和ObjectClass
Object可以认为是一个属性或者状态的集合,而ObjectClass则是方法或函数指针的集合,所以可以看到CPU的Object结构名称是CPUState。 struct ObjectClass是所有类的基类。
struct ObjectClass
{
/*< private >*/
Type type; /**/
GSList *interfaces; //该属性是接口链表节点
const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; //用于做cast操作时的缓存
const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent *unparent;
};
这个结构也是所有接口的基类,一个类可以实现多个接口,interface作为链表的head,该类实现的所有接口的InterfaceClass结构构成了链表。
struct Object是所有对象的基对象。
struct Object
{
/*< private >*/
ObjectClass *class; //指向对应的类的数据结构的指针
ObjectFree *free; //当引用计数为0时调用
GHashTable *properties; //Object中的所有属性的hash表
uint32_t ref; //对象的引用计数
Object *parent; //指向父对象的指针
};
列出的这些属性都是私有的。properties中的属性才是可被访问和修改的,这个hash表的每个entry是一个key value对,key是属性的名称,value是ObjectProperty结构,这个结构定义如下:
typedef struct ObjectProperty
{
gchar *name;
gchar *type;
gchar *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
void *opaque;
} ObjectProperty;
在QOM中,利用object_property_add函数可向一个对象中加入属性,这个函数通常在对象的构造函数中调用。同时QOM还提供了其他的get和set函数用于对属性的读写。
ObjectProperty *
object_property_add(Object *obj, const char *name, const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
ObjectPropertyRelease *release,
void *opaque, Error **errp)
{
ObjectProperty *prop;
size_t name_len = strlen(name);
if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) {
int i;
ObjectProperty *ret;
char *name_no_array = g_strdup(name);
name_no_array[name_len - 3] = '\0';
for (i = 0; ; ++i) {
char *full_name = g_strdup_printf("%s[%d]", name_no_array, i);
ret = object_property_add(obj, full_name, type, get, set,
release, opaque, NULL);
g_free(full_name);
if (ret) {
break;
}
}
g_free(name_no_array);
return ret;
}
if (g_hash_table_lookup(obj->properties, name) != NULL) {
error_setg(errp, "attempt to add duplicate property '%s'"
" to object (type '%s')", name,
object_get_typename(obj));
return NULL;
}
prop = g_malloc0(sizeof(*prop));
prop->name = g_strdup(name);
prop->type = g_strdup(type);
prop->get = get;
prop->set = set;
prop->release = release;
prop->opaque = opaque;
g_hash_table_insert(obj->properties, prop->name, prop);
return prop;
}
接口
在TypeImpl和TypeInfo中的InterfaceImpl和InterfaceInfo这两个结构是一样,只包含一个指示type的字符串,接口的数据结构是
struct InterfaceClass
{
ObjectClass parent_class; //它的父类就是ObjectClass
/*< private >*/
ObjectClass *concrete_class; //实现这个接口的类的指针
Type interface_type; //这个interface的类型(TypeImpl*指针),这个属性指示要实现的接口类型。
};
这个数据结构保持了类实际实现接口的方法的指针。 所有对接口都继承自一个特殊的空接口,用于快速检查当前的TypeImpl是否是一个接口。
动态cast
cast赋予了对象多态的能力,QOM中通过object_class_dynamic_cast函数进行动态的cast
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)) { //通过检查是否为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;
}
QOM的使用实例
创建一个新的类
这个类继承自DeviceClass
#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)
DEFINE_TYPES宏可以用来方便的注册多个typeinfo
static const TypeInfo device_types_info[] = {
{
.name = TYPE_MY_DEVICE_A,
.parent = TYPE_DEVICE,
.instance_size = sizeof(MyDeviceA),
},
{
.name = TYPE_MY_DEVICE_B,
.parent = TYPE_DEVICE,
.instance_size = sizeof(MyDeviceB),
},
};
定义三个宏方便进行cast
#define MY_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(MyDeviceClass, obj, TYPE_MY_DEVICE)
#define MY_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(MyDeviceClass, klass, TYPE_MY_DEVICE)
#define MY_DEVICE(obj) \
OBJECT_CHECK(MyDevice, obj, TYPE_MY_DEVICE)
类的初始化
这里的类的初始化是指类本身的初始化,而不是对象的初始化,类的初始化只会执行一次,并且保证一个类在执行初始化函数前,他的所有父类均被初始化。类的初始化化函数中可以对父类的虚函数进行重载。
#include "qdev.h"
void my_device_class_init(ObjectClass *klass, void *class_data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = my_device_reset;
}
static const TypeInfo my_device_info = {
.name = TYPE_MY_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(MyDevice),
.class_init = my_device_class_init, /*在类初始化时就会调用这个函数,将虚拟函数赋值*/
};
添加新的虚函数则需要类定义自己的结构并给TypeInfo中的class_size变量赋值,每个函数最好有一个wrapper函数方便调用。
#include "qdev.h"
typedef struct MyDeviceClass
{
DeviceClass parent;
void (*frobnicate) (MyDevice *obj);
} MyDeviceClass;
static const TypeInfo my_device_info = {
.name = TYPE_MY_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(MyDevice),
.abstract = true, // or set a default in my_device_class_init
.class_size = sizeof(MyDeviceClass),
};
void my_device_frobnicate(MyDevice *obj)
{
MyDeviceClass *klass = MY_DEVICE_GET_CLASS(obj);
klass->frobnicate(obj);
}
接口
接口是一个特殊的类,只有class而没有object,class中包含实现方法的函数指针。
重载函数
在QOM中,所有的函数实际上都是虚函数,可以被重载的,如果一个类重载了一个函数,之后对象调用该方法时会直接调用重载的函数,所以类有责任检查是否应该调用原函数。
typedef struct MyState MyState;
typedef void (*MyDoSomething)(MyState *obj);
typedef struct MyClass {
ObjectClass parent_class;
MyDoSomething do_something;
} MyClass;
static void my_do_something(MyState *obj)
{
// do something
}
static void my_class_init(ObjectClass *oc, void *data)
{
MyClass *mc = MY_CLASS(oc);
mc->do_something = my_do_something;
}
static const TypeInfo my_type_info = {
.name = TYPE_MY,
.parent = TYPE_OBJECT,
.instance_size = sizeof(MyState),
.class_size = sizeof(MyClass),
.class_init = my_class_init,
};
typedef struct DerivedClass {
MyClass parent_class;
MyDoSomething parent_do_something;
} DerivedClass;
static void derived_do_something(MyState *obj)
{
DerivedClass *dc = DERIVED_GET_CLASS(obj);
// do something here
dc->parent_do_something(obj);
// do something else here
}
static void derived_class_init(ObjectClass *oc, void *data)
{
MyClass *mc = MY_CLASS(oc);
DerivedClass *dc = DERIVED_CLASS(oc);
dc->parent_do_something = mc->do_something;
mc->do_something = derived_do_something;
}
static const TypeInfo derived_type_info = {
.name = TYPE_DERIVED,
.parent = TYPE_MY,
.class_size = sizeof(DerivedClass),
.class_init = derived_class_init,
};