qemu面向對象的模型:QOM
qemu用結構體實現了一套面向對象的機制,用於實現類和對象的概念,也實現了面向對象的封裝、繼承、多態。大部分設備模擬代碼都會基於這個qom模型。
相關數據結構:
類定義:
struct ObjectClass
{
/*< private >*/
Type type; /* typedef struct TypeImpl *Type */
GSList *interfaces;
const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];
const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent *unparent;
GHashTable *properties;
};
對象定義:
struct Object
{
/*< private >*/
ObjectClass *class;
ObjectFree *free; /* 當引用技術爲0時,清理垃圾的回調函數 */
GHashTable *properties; /* 屬性的Hash表*/
uint32_t ref; /* 對象引用計數 */
Object *parent;
};
屬性定義:
typedef struct ObjectProperty
{
gchar *name;
gchar *type;
gchar *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
void *opaque;
} ObjectProperty;
每個object對象會有上述對應的屬性結構,並用哈希表組織了起來。當需要增加或者刪除屬性時,可調用object_property_add或object_property_del函數。
定義一個類型:
struct TypeInfo
{
const char *name;
const char *parent;
size_t instance_size;
void (*instance_init)(Object *obj); /* 實例初始化 */
void (*instance_post_init)(Object *obj);
void (*instance_finalize)(Object *obj); /* 實例析構 */
bool abstract;
size_t class_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;
};
#define type_init(function) module_init(function, MODULE_INIT_QOM)
type_register函數會根據TypeInfo結構體生成TypeImpl結構體,並將name作爲key,創建全局hash表:
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];
};
類的基本信息是通過這個TypeImpl體現的
類的數據結構一般包含大量的函數指針,用於對對象進行操作。class對應的init函數可以將這些函數指針初始化。然後所有含有這個類數據結構指針的對象,都可以調用這些函數。 — 共性東西拿出來搞成Class,泛化
對象的數據結構一般包含了大量變量,是對象的一些屬性,很明顯這些是每個實例特有的,各自不盡相同。instance對應的init函數可以把這些屬性初始化,相當於C++中的構造函數。 — 特化的東西構成實例
初始化相關:
已virtio-net-pci爲例:
-
type_init(virtio_net_pci_register) -> register_module_init : 會將virtio_net_pci_register放到全局二維數組中init_type_list,數組成員爲ModuleEntry類型,內部有init函數指針,virtio_net_pci_register會賦值給init
-
main() -> module_call_init(MODULE_INIT_QOM) : 會遍歷init_type_list[MODULE_INIT_QOM]中的所有元素,並調用init]
-> virtio_net_pci_register -> type_register -> type_register_internal -> type_table_add [通過該調用鏈,對應TypeImpl結構被加入到全局hash表中 -
main -> select_machine -> object_class_get_list -> object_class_foreach ->object_class_get_list_tramp -> type_initialize
static void type_initialize(TypeImpl *ti) { .... if (ti->class) { /* 非空,已經初始化過,直接退出 */ return; } ... if (parent) { type_initialize(parent); /* 遞歸調用,回溯到根節點 */ ... memcpy(ti->class,parent->class,parent->class_size);/* 複製父節點的class結構體,繼承的實現本質 */ ... } ... if (ti->class_init) { ti->class_init(ti->class, ti->class_data); /* 類初始化函數 */ } }
【TypeImpl是一種輔助數據結構,類的創建以及初始化會基於該結構體】
-
main -> qemu_opts_foreach -> device_init_func -> device_init_func -> qdev_device_add -> object_new : 該調用鏈會遍歷全局保存的QemuOptsList數組(vm_config_groups),找到類型爲device的單元,例如對應下面的命令行選項:
-netdev tap,id=hostnet0,ifname=vnet2,downscript=no -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:6b:0d:a1,bus=pci.0,addr=0x3
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) { DeviceClass *dc; const char *driver, *path; DeviceState *dev; BusState *bus = NULL; ... driver = qemu_opt_get(opts, "driver"); /* 此處對應virtio-net-pci */ ... dc = qdev_get_device_class(&driver, errp); ... path = qemu_opt_get(opts, "bus"); /* 尋找對應總線路徑,此處對應pci.0 */ if (path != NULL) { bus = qbus_find(path, errp); /* 根據path尋找對應的bus實例 */ if (!bus) { return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { error_setg(errp, "Device '%s' can't go on %s bus", driver, object_get_typename(OBJECT(bus))); return NULL; } /* 總線不匹配 */ } ... dev = DEVICE(object_new(driver)); /* 創建設備實例 */ if (bus) { qdev_set_parent_bus(dev, bus); /* 設備與總線關聯 */ } ... if (qemu_opt_foreach(opts, set_property, dev, &err)) { goto err_del_dev; } /* 根據opts設置屬性,上例會設置的屬性有:netdev、mac、addr */ /* 對於netdev屬性,後面會調用set_netdev函數,使得device和netdev發生關聯,前面已經具體介紹過 */ dev->opts = opts; object_property_set_bool(OBJECT(dev), true, "realized", &err); /* 觸發realize回調 */ }
object_new -> object_new_with_type -> object_initialize_with_type -> object_initialize_with_type -> object_init_with_type
static void object_init_with_type(Object *obj, TypeImpl *ti) { if (type_has_parent(ti)) { object_init_with_type(obj, type_get_parent(ti));/* 回溯到根節點,遞歸調用 */ } if (ti->instance_init) { ti->instance_init(obj); /* 實例初始化函數 */ } }
-
另外關注一下DeviceClass這個結構體,其中有個realize回調,設備的許多初始化操作是在這個函數中進行的。
typedef struct DeviceClass { /*< private >*/ ObjectClass parent_class; /*< public >*/ DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); const char *fw_name; const char *desc; Property *props; bool user_creatable; bool hotpluggable; /* callbacks */ DeviceReset reset; DeviceRealize realize; /* 更具體的初始化操作 */ DeviceUnrealize unrealize; /* device state */ const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ const char *bus_type; };
以virtio_net_device_realize爲例:
main -> qemu_opts_foreach -> device_init_func -> qdev_device_add -> object_property_set_bool -> property_set_bool -> device_set_realized -> pci_qdev_realize -> virtio_pci_realize -> object_property_set_qobject -> property_set_bool -> device_set_realized -> virtio_net_device_realize
realize這個屬性是在object_init_with_type回溯到DeciceClass節點調用對應的instance_init實現的,對應的函數爲:
static void device_initfn(Object *obj)
{
....
object_property_add_bool(obj, "realized",
device_get_realized, device_set_realized, NULL);
....
}