淺談QEMU的對(duì)象系統(tǒng)

眾所周知,C語(yǔ)言并沒(méi)有原生的面向?qū)ο笙到y(tǒng),于是乎出現(xiàn)了各種奇妙的C語(yǔ)言面向?qū)ο蟮慕鉀Q方案,最有名的就是Linux內(nèi)核里面往對(duì)象里插struct **_operation{}作為多態(tài)支持的解決方案,這里QEMU為了實(shí)現(xiàn)對(duì)多種虛擬設(shè)備的支持,提供了一套基本工具作為解決方案,其實(shí)在里面可以看到很多C++的影子,就比如說(shuō)那個(gè)object_dynamic_cast_assert,就很C++。

QEMU對(duì)象系統(tǒng)的幾個(gè)關(guān)鍵結(jié)構(gòu)

當(dāng)然,并不意外的是,QEMU的對(duì)象支持也是以結(jié)構(gòu)體作為支持基礎(chǔ)的,它提供了大概Object、ObjectClass、TypeInfo、TypeImpl幾個(gè)關(guān)鍵數(shù)據(jù)結(jié)構(gòu),作為面向?qū)ο笙到y(tǒng)的基本支持。

下面是這幾個(gè)類型的UML圖:

qemu_qom (1).png

隨便找了個(gè)在線工具畫了個(gè)UML圖結(jié)果導(dǎo)出還有水印…水印你們湊合著看吧哈…可以看出,對(duì)象的核心系統(tǒng)是Object,其實(shí)也就是我們常說(shuō)的對(duì)象實(shí)例,這個(gè)實(shí)例實(shí)際上只保存了其指向的類型信息即ObjectClass,還有就是其基類的信息,即parent指針。而ObjectClass就厲害了,其保存了一個(gè)關(guān)鍵指針,也就是TypeImpl類型的指針,這就保證了他能夠訪問(wèn)到其類型的關(guān)鍵信息,包括類型名nameparent_typeparent,這對(duì)于尋找父類還是至關(guān)重要的。TypeImpl保存著最全的類型信息,不但包括類型名、類型大小、是否抽象類、基類名稱、基類TypeImpl指針、所指向的ObjectClass、還有InterfaceImpl的相關(guān)信息(InterfaceImpl的相關(guān)問(wèn)題我大概在后面寫)而TypeInfo我們可以觀察到,并沒(méi)有任何結(jié)構(gòu)與其相連,那么他是怎么和這三個(gè)結(jié)構(gòu)產(chǎn)生關(guān)聯(lián)的呢?其實(shí)TypeInfo是面向API使用者的一個(gè)工具類,使用者只是在注冊(cè)類型的時(shí)候,提供TypeInfo所包含的信息(包括方法中的回調(diào)函數(shù)),然后系統(tǒng)會(huì)自動(dòng)生成一個(gè)TypeImpl存儲(chǔ)起來(lái),至此TypeInfo的生命周期就結(jié)束了。

類型的注冊(cè)過(guò)程

一句話講類型注冊(cè)系統(tǒng):QOM維護(hù)了一個(gè)全局的類型哈希表,可以使用類型名字符串索引到具體的TypeImpl對(duì)象,這樣一個(gè)簡(jiǎn)單的類型索引系統(tǒng)就跑起來(lái)了。對(duì)于一個(gè)類管理系統(tǒng)而言,墜好的就是在系統(tǒng)初始化完成的時(shí)候,就已經(jīng)玩成了類管理系統(tǒng)本身的初始化,即所有類都注冊(cè)好并可以隨時(shí)調(diào)用。QEMU也適時(shí)地做到了這一點(diǎn),它提供了一個(gè)type_init的宏,而這個(gè)宏其實(shí)是module_init宏的一個(gè)殼子:

#define type_init(function) module_init(function, MODULE_INIT_QOM) 

而這個(gè)module_init則聲明如下:

static void __attribute__((constructor)) do_qemu_init_ ## function(void)     \
 {                                                                           \
     register_module_init(function, type);                                   \
 } 

這里我們主要關(guān)心這個(gè)__attribute__((constructor))這是一個(gè)gcc的擴(kuò)展,意味著這個(gè)函數(shù)在main函數(shù)調(diào)用之前就會(huì)被調(diào)用,這樣看來(lái),如果傳入的function函數(shù)名為kvm_accel_class_init,這個(gè)宏的作用就是生成了一個(gè)static void do_qemu_init_kvm_accel_class_init(void)的函數(shù),并保證其在main函數(shù)之前被調(diào)用,以達(dá)到自動(dòng)初始化的目的。這樣做的好處,其實(shí)也很明了,可以極大的方面模塊化的開發(fā),開發(fā)者只需要調(diào)用一個(gè)宏,就可以多聲明一個(gè)類,而不用去思考我聲明完成之后要去哪里哪里插一個(gè)初始化函數(shù)調(diào)用。(幽幽地望向辣雞net-snmp)。那么我們繼續(xù)看這個(gè)函數(shù),他做了什么呢?他只是簡(jiǎn)單地調(diào)用register_module_init(function, type);把這個(gè)類型初始化的function插入到類型鏈表中去了而已。這里就說(shuō)到QOM的全局module鏈表了,QOM有一個(gè)全局的init_type_list,顧名思義,他是類型的鏈表,其聲明為

QTAILQ_HEAD(, ModuleEntry) ModuTypelist; 
static ModuleTypeList init_type_list[MODULE_INIT_MAX];

復(fù)制代碼可以看出,這是一個(gè)鏈表的數(shù)組,而每個(gè)鏈表節(jié)點(diǎn),都是一個(gè)模塊類型的初始化函數(shù)指針,register_module_init的作用,就是找到對(duì)應(yīng)的下標(biāo),然后把這個(gè)初始化函數(shù)指針插入鏈表中。方便系統(tǒng)在初始化時(shí),調(diào)用module_call_init集中初始化。那么初始化函數(shù)中應(yīng)該做些什么呢?QOM提供了指導(dǎo),甚至,我們可以直接看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); 

他聲明了一個(gè)TypeInfo,塞了一些個(gè)人信息,然后單純地調(diào)用了type_register_static注冊(cè)類型。又是注冊(cè)類型?這里和之前module的初始化不一樣,type_register_staic創(chuàng)建了一個(gè)TypeImpl類型,然后把這個(gè)類型插入了一個(gè)全局的type_table中,key是其name成員,value則指向TypeImpl本身。這樣一套全局的類型注冊(cè)就完成了,簡(jiǎn)單而言,QOM維護(hù)了一個(gè)全局的類型哈希表,可以使用類型名字符串索引到具體的TypeImpl對(duì)象,這樣一個(gè)簡(jiǎn)單的類型索引系統(tǒng)就跑起來(lái)了。

類型的動(dòng)態(tài)轉(zhuǎn)換過(guò)程

QOM實(shí)現(xiàn)了簡(jiǎn)單的繼承機(jī)制,相應(yīng)地,QOM也提供了一套相對(duì)完整的類型轉(zhuǎn)換機(jī)制,以實(shí)現(xiàn)基類到子類、子類到基類的各種轉(zhuǎn)換。
這里其實(shí)是依賴內(nèi)存布局的,這里的布局與C++幾乎一致,子類型的內(nèi)存布局,起始都是基類型的成員,這樣子類轉(zhuǎn)基類只需要一個(gè)強(qiáng)制轉(zhuǎn)換就可以搞定了。
而基類轉(zhuǎn)子類就要稍微麻煩一些,你怎么知道這個(gè)基類對(duì)象就是這個(gè)子類對(duì)象的實(shí)例呢,轉(zhuǎn)錯(cuò)了你負(fù)責(zé)嗎?所幸也不是很麻煩,你看,我們的Object類型和TypeImpl不是都有parent指針嘛,我們還有可以根據(jù)類型名索引類型的哈希表,要查一下親子關(guān)系也不是很麻煩,向上遍歷parent,查到了你就是,查不到你就不是,就這么簡(jiǎn)單。
QEMU提供了以下幾個(gè)宏作為類型轉(zhuǎn)換的基礎(chǔ):

/**
 * 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) 

具體你們自己看吧,注釋都說(shuō)的很清楚了。基本上其類型轉(zhuǎn)換都是調(diào)用了兩個(gè)函數(shù):object_dynamic_cast_assertobject_class_dynamic_cast_assert,其分別調(diào)用了去掉cast的那個(gè)函數(shù),而前者則是調(diào)用了后者(不同的是前者有一個(gè)轉(zhuǎn)換緩存),后者所作,就是簡(jiǎn)單地向上追溯parent判斷其祖輩關(guān)系是否成立,能夠成立則返回傳入的obj自身并進(jìn)行強(qiáng)制轉(zhuǎn)換,這樣就完成了簡(jiǎn)單地動(dòng)態(tài)類型轉(zhuǎn)換,即dynamic_cast。

總結(jié)QEMU

其實(shí)實(shí)現(xiàn)了一套不錯(cuò)的對(duì)象管理系統(tǒng),包括自動(dòng)化的對(duì)象注冊(cè)、相對(duì)完善的對(duì)象管理和比較巧妙的動(dòng)態(tài)轉(zhuǎn)換方式,算是給C語(yǔ)言的OO系統(tǒng)提供了一套不錯(cuò)的思路,可惜受制于語(yǔ)言表達(dá)能力,其使用依賴大量的宏,要熟練地使用起這一套東西來(lái),心智負(fù)擔(dān)也并不小,學(xué)習(xí)曲線還是有些陡峭的,也算是美中不足吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容