众所周知,C语言并无原生的面向对象系统,因而乎出现了各类奇妙的C语言面向对象的解决方案,最有名的就是Linux内核里面往对象里插struct **_operation{}
做为多态支持的解决方案,这里QEMU为了实现对多种虚拟设备的支持,提供了一套基本工具做为解决方案,其实在里面能够看到不少C++的影子,就好比说那个object_dynamic_cast_assert
,就很C++。数组
固然,并不意外的是,QEMU的对象支持也是以结构体做为支持基础的,它提供了大概Object
、ObjectClass
、TypeInfo
、TypeImpl
几个关键数据结构,做为面向对象系统的基本支持。缓存
下面是这几个类型的UML图:bash
随便找了个在线工具画了个UML图结果导出还有水印…水印大家凑合着看吧哈…数据结构
能够看出,对象的核心系统是Object
,其实也就是咱们常说的对象实例,这个实例实际上只保存了其指向的类型信息即ObjectClass
,还有就是其基类的信息,即parent指针。app
而ObjectClass
就厉害了,其保存了一个关键指针,也就是TypeImpl
类型的指针,这就保证了他可以访问到其类型的关键信息,包括类型名name
,parent_type
和parent
,这对于寻找父类仍是相当重要的。ide
TypeImpl
保存着最全的类型信息,不但包括类型名、类型大小、是否抽象类、基类名称、基类TypeImpl
指针、所指向的ObjectClass
、还有InterfaceImpl
的相关信息(InterfaceImpl
的相关问题我大概在后面写)模块化
而TypeInfo
咱们能够观察到,并无任何结构与其相连,那么他是怎么和这三个结构产生关联的呢?其实TypeInfo
是面向API使用者的一个工具类,使用者只是在注册类型的时候,提供TypeInfo
所包含的信息(包括方法中的回调函数),而后系统会自动生成一个TypeImpl
存储起来,至此TypeInfo
的生命周期就结束了。函数
一句话讲类型注册系统:QOM维护了一个全局的类型哈希表,可使用类型名字符串索引到具体的TypeImpl对象,这样一个简单的类型索引系统就跑起来了。
工具
对于一个类管理系统而言,坠好的就是在系统初始化完成的时候,就已经玩成了类管理系统自己的初始化,即全部类都注册好并能够随时调用。QEMU也适时地作到了这一点,它提供了一个type_init的宏,而这个宏实际上是module_init宏的一个壳子:布局
#define type_init(function) module_init(function, MODULE_INIT_QOM) 复制代码
而这个module_init则声明以下:
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ { \ register_module_init(function, type); \ } 复制代码
这里咱们主要关心这个__attribute__((constructor))
这是一个gcc的扩展,意味着这个函数在main函数调用以前就会被调用,这样看来,若是传入的function函数名为kvm_accel_class_init
,这个宏的做用就是生成了一个static void do_qemu_init_kvm_accel_class_init(void)
的函数,并保证其在main函数以前被调用,以达到自动初始化的目的。
这样作的好处,其实也很明了,能够极大的方面模块化的开发,开发者只须要调用一个宏,就能够多声明一个类,而不用去思考我声明完成以后要去哪里哪里插一个初始化函数调用。(幽幽地望向辣鸡net-snmp)。
那么咱们继续看这个函数,他作了什么呢?他只是简单地调用register_module_init(function, type);
把这个类型初始化的function
插入到类型链表中去了而已。
这里就说到QOM的全局module链表了,QOM有一个全局的init_type_list
,顾名思义,他是类型的链表,其声明为
QTAILQ_HEAD(, ModuleEntry) ModuTypelist;
static ModuleTypeList init_type_list[MODULE_INIT_MAX];复制代码
能够看出,这是一个链表的数组,而每一个链表节点,都是一个模块类型的初始化函数指针,register_module_init
的做用,就是找到对应的下标,而后把这个初始化函数指针插入链表中。方便系统在初始化时,调用module_call_init
集中初始化。
那么初始化函数中应该作些什么呢?
QOM提供了指导,甚至,咱们能够直接看KVM是怎么作的:
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
static void kvm_type_init(void)
{
type_register_static(&kvm_accel_type);
}
type_init(kvm_type_init);
复制代码
他声明了一个TypeInfo,塞了一些我的信息,而后单纯地调用了type_register_static
注册类型。
又是注册类型?这里和以前module的初始化不同,type_register_staic
建立了一个TypeImpl类型,而后把这个类型插入了一个全局的type_table
中,key是其name
成员,value则指向TypeImpl
自己。
这样一套全局的类型注册就完成了,简单而言,QOM维护了一个全局的类型哈希表,可使用类型名字符串索引到具体的TypeImpl对象,这样一个简单的类型索引系统就跑起来了。
QOM实现了简单的继承机制,相应地,QOM也提供了一套相对完整的类型转换机制,以实现基类到子类、子类到基类的各类转换。
这里实际上是依赖内存布局的,这里的布局与C++几乎一致,子类型的内存布局,起始都是基类型的成员,这样子类转基类只须要一个强制转换就能够搞定了。
而基类转子类就要稍微麻烦一些,你怎么知道这个基类对象就是这个子类对象的实例呢,转错了你负责吗?所幸也不是很麻烦,你看,咱们的Object
类型和TypeImpl
不是都有parent
指针嘛,咱们还有能够根据类型名索引类型的哈希表,要查一下亲子关系也不是很麻烦,向上遍历parent
,查到了你就是,查不到你就不是,就这么简单。
QEMU提供了如下几个宏做为类型转换的基础:
/**
* OBJECT:
* @obj: A derivative of #Object
*
* Converts an object to a #Object. Since all objects are #Objects,
* this function will always succeed.
*/
#define OBJECT(obj) \
((Object *)(obj))
/**
* OBJECT_CLASS:
* @class: A derivative of #ObjectClass.
*
* Converts a class to an #ObjectClass. Since all objects are #Objects,
* this function will always succeed.
*/
#define OBJECT_CLASS(class) \
((ObjectClass *)(class))
/**
* OBJECT_CHECK:
* @type: The C type to use for the return value.
* @obj: A derivative of @type to cast.
* @name: The QOM typename of @type
*
* A type safe version of @object_dynamic_cast_assert. Typically each class
* will define a macro based on this type to perform type safe dynamic_casts to
* this object type.
*
* If an invalid object is passed to this function, a run time assert will be
* generated.
*/
#define OBJECT_CHECK(type, obj, name) \
((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \
__FILE__, __LINE__, __func__))
/**
* OBJECT_CLASS_CHECK:
* @class_type: The C type to use for the return value.
* @class: A derivative class of @class_type to cast.
* @name: the QOM typename of @class_type.
*
* A type safe version of @object_class_dynamic_cast_assert. This macro is
* typically wrapped by each type to perform type safe casts of a class to a
* specific class type.
*/
#define OBJECT_CLASS_CHECK(class_type, class, name) \
((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
__FILE__, __LINE__, __func__))
/**
* OBJECT_GET_CLASS:
* @class: The C type to use for the return value.
* @obj: The object to obtain the class for.
* @name: The QOM typename of @obj.
*
* This function will return a specific class for a given object. Its generally
* used by each type to provide a type safe macro to get a specific class type
* from an object.
*/
#define OBJECT_GET_CLASS(class, obj, name) \
OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name) 复制代码
具体大家本身看吧,注释都说的很清楚了。基本上其类型转换都是调用了两个函数:
object_dynamic_cast_assert
和object_class_dynamic_cast_assert
,其分别调用了去掉cast的那个函数,而前者则是调用了后者(不一样的是前者有一个转换缓存),后者所做,就是简单地向上追溯parent判断其祖辈关系是否成立,可以成立则返回传入的obj自身并进行强制转换,这样就完成了简单地动态类型转换,即dynamic_cast
。
QEMU其实实现了一套不错的对象管理系统,包括自动化的对象注册、相对完善的对象管理和比较巧妙的动态转换方式,算是给C语言的OO系统提供了一套不错的思路,惋惜受制于语言表达能力,其使用依赖大量的宏,要熟练地使用起这一套东西来,心智负担也并不小,学习曲线仍是有些陡峭的,也算是美中不足吧。