接前一篇文章:QEMU源码全解析16 —— QOM介绍(5)
本文内容参考:
《趣谈Linux操作系统》 —— 刘超,极客时间
《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的第二部分 —— 类型的初始化就介绍完了。