QEMU源码全解析17 —— QOM介绍(6)

88 篇文章 19 订阅
本文详细解析QEMU源码中QOM的类型初始化过程,通过函数调用链揭示了如何通过`type_initialize`递归初始化所有类。从`qemu_create_machine`开始,经过`select_machine`、`find_default_machine`,到`object_class_get_list`,最终通过`object_class_foreach_tramp`完成类型初始化。
摘要由CSDN通过智能技术生成

接前一篇文章: 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)中,代码如下:

  1. static void qemu_create_machine(QDict *qdict)
  2. {
  3. MachineClass *machine_class = select_machine(qdict, &error_fatal);
  4. object_set_machine_compat_props(machine_class->compat_props);
  5. current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class)));
  6. object_property_add_child(object_get_root(), "machine",
  7. OBJECT(current_machine));
  8. object_property_add_child(container_get(OBJECT(current_machine),
  9. "/unattached"),
  10. "sysbus", OBJECT(sysbus_get_default()));
  11. if (machine_class->minimum_page_bits) {
  12. if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) {
  13. /* This would be a board error: specifying a minimum smaller than
  14. * a target's compile-time fixed setting.
  15. */
  16. g_assert_not_reached();
  17. }
  18. }
  19. cpu_exec_init_all();
  20. page_size_init();
  21. if (machine_class->hw_version) {
  22. qemu_set_hw_version(machine_class->hw_version);
  23. }
  24. /*
  25. * Get the default machine options from the machine if it is not already
  26. * specified either by the configuration file or by the command line.
  27. */
  28. if (machine_class->default_machine_opts) {
  29. QDict *default_opts =
  30. keyval_parse(machine_class->default_machine_opts, NULL, NULL,
  31. &error_abort);
  32. qemu_apply_legacy_machine_options(default_opts);
  33. object_set_properties_from_keyval(OBJECT(current_machine), default_opts,
  34. false, &error_abort);
  35. qobject_unref(default_opts);
  36. }
  37. }

函数一开始调用了select_machine函数,该函数仍然在同文件中,代码如下:

  1. static MachineClass *select_machine(QDict *qdict, Error **errp)
  2. {
  3. const char *optarg = qdict_get_try_str(qdict, "type");
  4. GSList *machines = object_class_get_list(TYPE_MACHINE, false);
  5. MachineClass *machine_class;
  6. Error *local_err = NULL;
  7. if (optarg) {
  8. machine_class = find_machine(optarg, machines);
  9. qdict_del(qdict, "type");
  10. if (!machine_class) {
  11. error_setg(&local_err, "unsupported machine type");
  12. }
  13. } else {
  14. machine_class = find_default_machine(machines);
  15. if (!machine_class) {
  16. error_setg(&local_err, "No machine specified, and there is no default");
  17. }
  18. }
  19. g_slist_free(machines);
  20. if (local_err) {
  21. error_append_hint(&local_err, "Use -machine help to list supported machines\n");
  22. error_propagate(errp, local_err);
  23. }
  24. return machine_class;
  25. }

进而由select_machine函数中的find_default_machine函数来找默认的machine类型。fine_default_machine函数也是在softmmu/vl.c中,代码如下:

  1. static MachineClass *find_default_machine(GSList *machines)
  2. {
  3. GSList *el;
  4. MachineClass *default_machineclass = NULL;
  5. for (el = machines; el; el = el->next) {
  6. MachineClass *mc = el->data;
  7. if (mc->is_default) {
  8. assert(default_machineclass == NULL && "Multiple default machines");
  9. default_machineclass = mc;
  10. }
  11. }
  12. return default_machineclass;
  13. }

回到上一级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中,代码如下:

  1. GSList *object_class_get_list(const char *implements_type,
  2. bool include_abstract)
  3. {
  4. GSList *list = NULL;
  5. object_class_foreach(object_class_get_list_tramp,
  6. implements_type, include_abstract, &list);
  7. return list;
  8. }

object_class_foreach函数在同文件中,代码如下:

  1. void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
  2. const char *implements_type, bool include_abstract,
  3. void *opaque)
  4. {
  5. OCFData data = { fn, implements_type, include_abstract, opaque };
  6. enumerating_types = true;
  7. g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data);
  8. enumerating_types = false;
  9. }

可以看到,object_class_foreach函数会对type_table中所有类型调用object_class_foreach_tramp函数,即最终会对类型哈希表type_table中的每一个元素调用object_class_foreach_tramp函数。

object_class_foreach_tramp函数仍然在qom/object.c中,代码如下:

  1. static void object_class_foreach_tramp(gpointer key, gpointer value,
  2. gpointer opaque)
  3. {
  4. OCFData *data = opaque;
  5. TypeImpl *type = value;
  6. ObjectClass *k;
  7. type_initialize(type);
  8. k = type->class;
  9. if (!data->include_abstract && type->abstract) {
  10. return;
  11. }
  12. if (data->implements_type &&
  13. !object_class_dynamic_cast(k, data->implements_type)) {
  14. return;
  15. }
  16. data->fn(k, data->opaque);
  17. }

object_class_foreach_tramp函数中会调用type_initialize函数。这样,在执行select_machine函数的时候就顺带把所有类型都初始化了。

至此,QOM的第二部分 —— 类型的初始化就介绍完了。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他