浅谈QEMU的对象系统

浅谈QEMU的对象系统

众所周知,C语言并没有原生的面向对象系统,于是乎出现了各种奇妙的C语言面向对象的解决方案,最有名的就是Linux内核里面往对象里插struct **_operation{}作为多态支持的解决方案,这里QEMU为了实现对多种虚拟设备的支持,提供了一套基本工具作为解决方案,其实在里面可以看到很多C++的影子,就比如说那个object_dynamic_cast_assert,就很C++。

QEMU对象系统的几个关键结构

当然,并不意外的是,QEMU的对象支持也是以结构体作为支持基础的,它提供了大概Object、ObjectClass、TypeInfo、TypeImpl几个关键数据结构,作为面向对象系统的基本支持。

下面是这几个类型的UML图:

随便找了个在线工具画了个UML图结果导出还有水印…水印你们凑合着看吧哈…

可以看出,对象的核心系统是Object,其实也就是我们常说的对象实例,这个实例实际上只保存了其指向的类型信息即ObjectClass,还有就是其基类的信息,即parent指针。

而ObjectClass就厉害了,其保存了一个关键指针,也就是TypeImpl类型的指针,这就保证了他能够访问到其类型的关键信息,包括类型名name,parent_type和parent,这对于寻找父类还是至关重要的。

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系统提供了一套不错的思路,可惜受制于语言表达能力,其使用依赖大量的宏,要熟练地使用起这一套东西来,心智负担也并不小,学习曲线还是有些陡峭的,也算是美中不足吧。

转载于:https://juejin.im/post/5cdd596cf265da03b9183eca

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100916.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java integer范围值的大小_求最大值最小值的代码

    java integer范围值的大小_求最大值最小值的代码java中的Integer.MAX_VALUE和Integer.MIN_VLAUE最近在刷leetcode的题时,才发现有几道题的利用到Integer类型的最大值和最小值,尤其是在判断是否溢出的时候,有道题就非常经典直接判断最后一位,比如最大值231-1的最后一位是7,而最小值-231的最后一位是8,这样进行一个判断8.字符串转换整数(atoi)这道题对我在面试过程中被问到如何判…

    2022年9月8日
  • 学生模式 在华为EMUI9.0上改成 健康使用手机背后的原因?

    学生模式 在华为EMUI9.0上改成 健康使用手机背后的原因?生活中,孩子用手机搜题、看剧、查资料、玩游戏很常见,监护人的合理引导是关键,EMUI9.0健康使用手机,是上一代【学生模式】的升级版。最大的区别是【学生模式】是让孩子在预设的时间段使用【健康使用手机】更注重于时间的平衡安排,限制手机或应用的可用时长通过合理引导加控制,让孩子健康享受科技的乐趣华为手机学生模式改成健康使用手机背后的原因?我想,EMUI9.0倡导的质享生活,就是管理好你的…

    2022年6月8日
  • C++ 核心编程

    C++核心编程引用**作用:**给变量起别名语法:数据类型&别名=原名引用是一种特殊的指针类型,引用一旦被定义就不能重新赋值,并且不能被设置为空值。intmain()

    2021年12月13日
  • 怎么使用Java 8 Stream将List(Object)转成List(Map(String, Object))?

    怎么使用Java 8 Stream将List(Object)转成List(Map(String, Object))?问题描述:有一个用户列表List<User>,须将每个User部分字段提取出来,重新放入一个Map中,然后将生成的Map放入List中。原来代码如下:publicstaticList<Map<String,Object>>toListMap(List<User>userList){List<Map<Stri…………

    2022年9月3日
  • 明天准备离职了,面对照顾自己的领导,要这样说[通俗易懂]

    明天准备离职了,面对照顾自己的领导,要这样说

    2022年2月13日
  • 手机人才需要“山寨”精神[通俗易懂]

    手机人才需要“山寨”精神[通俗易懂] 山寨,颇具江湖味道的词语,隐藏了相当多的非主流文化因素,山寨机的横行使得手机江湖更加暗涛汹涌。由芯片厂商提供集成了软件的芯片,再配上外壳和电池,任何想自立为王的手机厂商用两个月就可以制造出这种移动终端,而正规手机厂商生产没有个一年半载却出不来。山寨机的繁荣给许多国产手机的正规军造成了干扰。一些正规手机企业指责山寨机由于缺乏核心技术和自主创新能力,长期发展下去会拖累中国在手机制造领域的发展水平。不

    2022年9月24日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号