接前一篇文章: QEMU源码全解析16 —— QOM介绍(5)
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一回讲解了QOM的第二部分 —— 类型的初始化,重点分析了type_initialize函数。类的初始化是通过type_initialize函数完成的。下边以其中一条路径来看type_initialize函数的调用过程。
通过上一回的type_initialize函数代码可以知道,其是static的,也就是说仅在qom/object.c文件内有效。这就好办多了,直接在文件中搜索type_initialize关键字就好了。一共有以下几处调用:
(1)type_initialize() -> type_initialize()
这就是上一回讲的递归初始化所有父类型。
(2)object_initialize_with_type() -> type_initialize()
(3)object_new_with_type() -> type_initialize()
(4)object_class_by_name() -> type_initialize()
(5)object_class_get_parent() -> type_initialize()
(6)object_class_foreach_tramp() -> type_initialize()
(7)type_initialize_interface() -> type_initialize()
下边以其中一条路径来看type_initialize的调用过程。
QEMU 会在qemu_init函数中调用qemu_create_machine函数。代码如下:
qemu_create_machine(machine_opts_dict);
qemu_create_machine函数在同文件(softmmu/vl.c)中,代码如下:
- static void qemu_create_machine(QDict *qdict)
- {
- MachineClass *machine_class = select_machine(qdict, &error_fatal);
- object_set_machine_compat_props(machine_class->compat_props);
-
- current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class)));
- object_property_add_child(object_get_root(), "machine",
- OBJECT(current_machine));
- object_property_add_child(container_get(OBJECT(current_machine),
- "/unattached"),
- "sysbus", OBJECT(sysbus_get_default()));
-
- if (machine_class->minimum_page_bits) {
- if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) {
- /* This would be a board error: specifying a minimum smaller than
- * a target's compile-time fixed setting.
- */
- g_assert_not_reached();
- }
- }
-
- cpu_exec_init_all();
- page_size_init();
-
- if (machine_class->hw_version) {
- qemu_set_hw_version(machine_class->hw_version);
- }
-
- /*
- * Get the default machine options from the machine if it is not already
- * specified either by the configuration file or by the command line.
- */
- if (machine_class->default_machine_opts) {
- QDict *default_opts =
- keyval_parse(machine_class->default_machine_opts, NULL, NULL,
- &error_abort);
- qemu_apply_legacy_machine_options(default_opts);
- object_set_properties_from_keyval(OBJECT(current_machine), default_opts,
- false, &error_abort);
- qobject_unref(default_opts);
- }
- }
函数一开始调用了select_machine函数,该函数仍然在同文件中,代码如下:
- static MachineClass *select_machine(QDict *qdict, Error **errp)
- {
- const char *optarg = qdict_get_try_str(qdict, "type");
- GSList *machines = object_class_get_list(TYPE_MACHINE, false);
- MachineClass *machine_class;
- Error *local_err = NULL;
-
- if (optarg) {
- machine_class = find_machine(optarg, machines);
- qdict_del(qdict, "type");
- if (!machine_class) {
- error_setg(&local_err, "unsupported machine type");
- }
- } else {
- machine_class = find_default_machine(machines);
- if (!machine_class) {
- error_setg(&local_err, "No machine specified, and there is no default");
- }
- }
-
- g_slist_free(machines);
- if (local_err) {
- error_append_hint(&local_err, "Use -machine help to list supported machines\n");
- error_propagate(errp, local_err);
- }
- return machine_class;
- }
进而由select_machine函数中的find_default_machine函数来找默认的machine类型。fine_default_machine函数也是在softmmu/vl.c中,代码如下:
- static MachineClass *find_default_machine(GSList *machines)
- {
- GSList *el;
- MachineClass *default_machineclass = NULL;
-
- for (el = machines; el; el = el->next) {
- MachineClass *mc = el->data;
-
- if (mc->is_default) {
- assert(default_machineclass == NULL && "Multiple default machines");
- default_machineclass = mc;
- }
- }
-
- return default_machineclass;
- }
回到上一级select_machine函数中。该函数中有这样一行代码:
GSList *machines = object_class_get_list(TYPE_MACHINE, false);
select_machine函数调用object_class_get_list函数以得到所有TYPE_MACHINE类型组成的链表。
oject_class_get_list函数在qom/object.c中,代码如下:
- GSList *object_class_get_list(const char *implements_type,
- bool include_abstract)
- {
- GSList *list = NULL;
-
- object_class_foreach(object_class_get_list_tramp,
- implements_type, include_abstract, &list);
- return list;
- }
object_class_foreach函数在同文件中,代码如下:
- void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
- const char *implements_type, bool include_abstract,
- void *opaque)
- {
- OCFData data = { fn, implements_type, include_abstract, opaque };
-
- enumerating_types = true;
- g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data);
- enumerating_types = false;
- }
可以看到,object_class_foreach函数会对type_table中所有类型调用object_class_foreach_tramp函数,即最终会对类型哈希表type_table中的每一个元素调用object_class_foreach_tramp函数。
object_class_foreach_tramp函数仍然在qom/object.c中,代码如下:
- static void object_class_foreach_tramp(gpointer key, gpointer value,
- gpointer opaque)
- {
- OCFData *data = opaque;
- TypeImpl *type = value;
- ObjectClass *k;
-
- type_initialize(type);
- k = type->class;
-
- if (!data->include_abstract && type->abstract) {
- return;
- }
-
- if (data->implements_type &&
- !object_class_dynamic_cast(k, data->implements_type)) {
- return;
- }
-
- data->fn(k, data->opaque);
- }
object_class_foreach_tramp函数中会调用type_initialize函数。这样,在执行select_machine函数的时候就顺带把所有类型都初始化了。
至此,QOM的第二部分 —— 类型的初始化就介绍完了。