淺談QEMU的對象系統

衆所周知,C語言並無原生的面向對象系統,因而乎出現了各類奇妙的C語言面向對象的解決方案,最有名的就是Linux內核裏面往對象裏插struct **_operation{}做爲多態支持的解決方案,這裏QEMU爲了實現對多種虛擬設備的支持,提供了一套基本工具做爲解決方案,其實在裏面能夠看到不少C++的影子,就好比說那個object_dynamic_cast_assert,就很C++。數組

QEMU對象系統的幾個關鍵結構

固然,並不意外的是,QEMU的對象支持也是以結構體做爲支持基礎的,它提供了大概ObjectObjectClassTypeInfoTypeImpl幾個關鍵數據結構,做爲面向對象系統的基本支持。緩存

下面是這幾個類型的UML圖:bash


隨便找了個在線工具畫了個UML圖結果導出還有水印…水印大家湊合着看吧哈…數據結構

能夠看出,對象的核心繫統是Object,其實也就是咱們常說的對象實例,這個實例實際上只保存了其指向的類型信息即ObjectClass,還有就是其基類的信息,即parent指針。app

ObjectClass就厲害了,其保存了一個關鍵指針,也就是TypeImpl類型的指針,這就保證了他可以訪問到其類型的關鍵信息,包括類型名nameparent_typeparent,這對於尋找父類仍是相當重要的。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_assertobject_class_dynamic_cast_assert,其分別調用了去掉cast的那個函數,而前者則是調用了後者(不一樣的是前者有一個轉換緩存),後者所做,就是簡單地向上追溯parent判斷其祖輩關係是否成立,可以成立則返回傳入的obj自身並進行強制轉換,這樣就完成了簡單地動態類型轉換,即dynamic_cast

總結

QEMU其實實現了一套不錯的對象管理系統,包括自動化的對象註冊、相對完善的對象管理和比較巧妙的動態轉換方式,算是給C語言的OO系統提供了一套不錯的思路,惋惜受制於語言表達能力,其使用依賴大量的宏,要熟練地使用起這一套東西來,心智負擔也並不小,學習曲線仍是有些陡峭的,也算是美中不足吧。

相關文章
相關標籤/搜索