QEMU源码全解析42 —— Machine(12)

88 篇文章 19 订阅
本文详细解析了QEMU中Machine类的注册与初始化过程,包括`type_register`函数的调用链,从`type_register_internal`到`type_new`,再到`type_table_add`。讲解了MachineClass的生成,以及如何从纸面状态转换为实际对象,涉及QEMU的类定义、全局哈希表和对象创建。通过对QEMU源码的分析,展示了QEMU如何模拟设备类并创建实例。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析41 —— Machine(11)

本文内容参考:

《趣谈 Linux操作系统 》 —— 刘超, 极客时间

QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社

特此致谢!

上一回针对于“Machine”系列第3~10篇文章开始梳理其脉络,梳理了type_init()这一条线,本文讲梳理type_register()这一条线,并对整个流程进行总结。

先再次贴出

  1. DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL,
  2. pc_i440fx_8_1_machine_options);

的相关代码:

  1. static void pc_init_v8_1(MachineState *machine)
  2. {
  3. void (*compat)(MachineState *m) = (NULL);
  4. if (compat) {
  5. compat(machine);
  6. }
  7. pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
  8. TYPE_I440FX_PCI_DEVICE);
  9. }
  10. static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
  11. {
  12. MachineClass *mc = MACHINE_CLASS(oc);
  13. pc_i440fx_8_1_machine_options(mc);
  14. mc->init = pc_init_v8_1;
  15. }
  16. static const TypeInfo pc_machine_type_v8_1 = {
  17. .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
  18. .parent = TYPE_PC_MACHINE,
  19. .class_init = pc_machine_v8_1_class_init,
  20. };
  21. static void pc_machine_init_v8_1(void)
  22. {
  23. type_register(&pc_machine_type_v8_1);
  24. }
  25. type_init(pc_machine_init_v8_1)

type_register函数在qom/object.c中,代码如下:

  1. TypeImpl *type_register(const TypeInfo *info)
  2. {
  3. assert(info->parent);
  4. return type_register_internal(info);
  5. }

type_register_internal函数就在上边,代码如下:

  1. static TypeImpl *type_register_internal(const TypeInfo *info)
  2. {
  3. TypeImpl *ti;
  4. ti = type_new(info);
  5. type_table_add(ti);
  6. return ti;
  7. }

type_new函数也在qom/object.c中,代码如下:

  1. static TypeImpl *type_new(const TypeInfo *info)
  2. {
  3. TypeImpl *ti = g_malloc0(sizeof(*ti));
  4. int i;
  5. g_assert(info->name != NULL);
  6. if (type_table_lookup(info->name) != NULL) {
  7. fprintf(stderr, "Registering `%s' which already exists\n", info->name);
  8. abort();
  9. }
  10. ti->name = g_strdup(info->name);
  11. ti->parent = g_strdup(info->parent);
  12. ti->class_size = info->class_size;
  13. ti->instance_size = info->instance_size;
  14. ti->instance_align = info->instance_align;
  15. ti->class_init = info->class_init;
  16. ti->class_base_init = info->class_base_init;
  17. ti->class_data = info->class_data;
  18. ti->instance_init = info->instance_init;
  19. ti->instance_post_init = info->instance_post_init;
  20. ti->instance_finalize = info->instance_finalize;
  21. ti->abstract = info->abstract;
  22. for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
  23. ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
  24. }
  25. ti->num_interfaces = i;
  26. return ti;
  27. }

type_table_add函数也在qom/object.c中,代码如下:

  1. static void type_table_add(TypeImpl *ti)
  2. {
  3. assert(!enumerating_types);
  4. g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
  5. }

每一个Module既然要模拟某种设备,那么就应该定义一种类型TypeImpl来表示这个设备。这其实是一种 面向对象编程 的思路,只不过这里用的是纯C语言的实现,因此需要变相实现一下类和对象。

  1. static void pc_machine_init_v8_1(void)
  2. {
  3. type_register(&pc_machine_type_v8_1);
  4. }
  5. type_init(pc_machine_init_v8_1)

pc_machine_init_v8_1函数会注册pc_machine_type_v8_1,可以认为这样就动态定义了一个类。

  1. static const TypeInfo pc_machine_type_v8_1 = {
  2. .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
  3. .parent = TYPE_PC_MACHINE,
  4. .class_init = pc_machine_v8_1_class_init,
  5. };

这个类的名字是“"pc-i440fx-8.1" TYPE_MACHINE_SUFFIX”,即"pc-i440fx-8.1-machine";这个类有父类TYPE_PC_MACHINE(即"generic-pc-machine");这个类的初始化应该调用函数pc_machine_v8_1_class_init。

这里的调用链为:

pc_machine_init_v8_1

type_register

type_register_internal

  1. static void pc_machine_init_v8_1(void)
  2. {
  3. type_register(&pc_machine_type_v8_1);
  4. }
  1. static TypeImpl *type_register_internal(const TypeInfo *info)
  2. {
  3. TypeImpl *ti;
  4. ti = type_new(info);
  5. type_table_add(ti);
  6. return ti;
  7. }

在type_register_internal函数中,会根据pc_machine_type_v8_1这个TypeInfo ,创建一个TypeImpl来表示这个新注册的类,也就是说,TypeImpl才是真正想要声明的那个class。分别来看一下TypeInfo结构和TypeImpl结构的定义:

struct TypeInfo的定义在include/qom/object.h中,如下:

  1. struct TypeInfo
  2. {
  3. const char *name;
  4. const char *parent;
  5. size_t instance_size;
  6. size_t instance_align;
  7. void (*instance_init)(Object *obj);
  8. void (*instance_post_init)(Object *obj);
  9. void (*instance_finalize)(Object *obj);
  10. bool abstract;
  11. size_t class_size;
  12. void (*class_init)(ObjectClass *klass, void *data);
  13. void (*class_base_init)(ObjectClass *klass, void *data);
  14. void *class_data;
  15. InterfaceInfo *interfaces;
  16. };

struct TypeImpl的定义在qom/object.c中,如下:

  1. struct TypeImpl
  2. {
  3. const char *name;
  4. size_t class_size;
  5. size_t instance_size;
  6. size_t instance_align;
  7. void (*class_init)(ObjectClass *klass, void *data);
  8. void (*class_base_init)(ObjectClass *klass, void *data);
  9. void *class_data;
  10. void (*instance_init)(Object *obj);
  11. void (*instance_post_init)(Object *obj);
  12. void (*instance_finalize)(Object *obj);
  13. bool abstract;
  14. const char *parent;
  15. TypeImpl *parent_type;
  16. ObjectClass *class;
  17. int num_interfaces;
  18. InterfaceImpl interfaces[MAX_INTERFACES];
  19. };

由定义可见,TypeInfo结构和TypeImpl结构还是很相近的。

QEMU 中,有一个全局的哈希表type_table,用来存放所有定义的类。在type_new函数中,先从type_table表中根据名字查找某个类(此处是pc_machine_type_v8_1)。如果找到,说明这个类已经被注册过了,报错并终止当前进程;如果没有找到,则说明这是一个新的类,那么就将TypeInfo里边的信息填到TypeImpl中。

  1. static TypeImpl *type_new(const TypeInfo *info)
  2. {
  3. TypeImpl *ti = g_malloc0(sizeof(*ti));
  4. int i;
  5. g_assert(info->name != NULL);
  6. if (type_table_lookup(info->name) != NULL) {
  7. fprintf(stderr, "Registering `%s' which already exists\n", info->name);
  8. abort();
  9. }
  10. ti->name = g_strdup(info->name);
  11. ti->parent = g_strdup(info->parent);
  12. ti->class_size = info->class_size;
  13. ti->instance_size = info->instance_size;
  14. ti->instance_align = info->instance_align;
  15. ti->class_init = info->class_init;
  16. ti->class_base_init = info->class_base_init;
  17. ti->class_data = info->class_data;
  18. ti->instance_init = info->instance_init;
  19. ti->instance_post_init = info->instance_post_init;
  20. ti->instance_finalize = info->instance_finalize;
  21. ti->abstract = info->abstract;
  22. for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
  23. ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
  24. }
  25. ti->num_interfaces = i;
  26. return ti;
  27. }

之后,type_register_internal函数会调用type_table_add函数,将这个类注册到全局的表中。

  1. static TypeImpl *type_register_internal(const TypeInfo *info)
  2. {
  3. TypeImpl *ti;
  4. ti = type_new(info);
  5. type_table_add(ti);
  6. return ti;
  7. }

到这里,class_init还没有被调用,也即这个类还处于纸面的状态。

此处与Java中的反射机制有些类似。在Java中,对于一个类,首先写代码的时候要写一个class xxx的定义,编译好后就放在.class文件中,这也是处于纸面的状态。然后,Java中会有一个Class对象,用于读取和表示这个纸面上的class xxx,从而生成真正的对象。

在QEMU中,也会有相类似的过程。class_init会生成XXXClass,相当于Java中的Class xxx;TypeImpl中还会有一个instance_init函数,相当于构造函数,用于根据XXXClass生成Object,这就相当于Java反射中最终创建的对象。和构造函数对应的还有instance_finalize,相当于析构函数。

分析完了type_register这一支,正式开始解析主流程。

在QEMU的老版本中,主函数main中直接调用select_machine函数,而在新版本中,则是如下调用流程:

main()

qemu_main()

qemu_init()

qemu_create_machine

select_machine()

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. }

其中第1行代码就是select_machine函数,代码片段如下:

    MachineClass *machine_class = select_machine(qdict, &error_fatal);

顾名思义,select_machine函数的作用是选择一个MachineClass,其可能由用户指定,如果用户未指定,则采用系统默认。如果是后者,QEMU最新版本号对应的机器类型为默认设置。由于笔者的源码为qemu-8.1.4,因此默认机器类型是pc-i440fx-8.1-machine。而这就对应于:

  1. static const TypeInfo pc_machine_type_v8_1 = {
  2. .name = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,
  3. .parent = TYPE_PC_MACHINE,
  4. .class_init = pc_machine_v8_1_class_init,
  5. };
  6. static void pc_machine_init_v8_1(void)
  7. {
  8. type_register(&pc_machine_type_v8_1);
  9. }

select_machine函数同样在softmmu/vl.c中,代码如下:

  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函数中,有两种方式可以生成MachineClass:一种方式是调用find_machine函数,通过解析QEMU命令行参数生成MachineClass,即用户指定方式;另一种方式是通过find_default_machine函数找一个默认的MachineClass,即系统默认方式。

无论是用户指定还是系统默认方式,都得先调用object_class_get_list函数获得一个MachineClass列表,然后在里边找。代码片段如下:

    GSList *machines = object_class_get_list(TYPE_MACHINE, false);

object_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. }

在全局表type_table_get()中,对于每一项TypeImpl,都执行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函数,该函数在同文件中,代码如下:

  1. static void type_initialize(TypeImpl *ti)
  2. {
  3. TypeImpl *parent;
  4. if (ti->class) {
  5. return;
  6. }
  7. ti->class_size = type_class_get_size(ti);
  8. ti->instance_size = type_object_get_size(ti);
  9. /* Any type with zero instance_size is implicitly abstract.
  10. * This means interface types are all abstract.
  11. */
  12. if (ti->instance_size == 0) {
  13. ti->abstract = true;
  14. }
  15. if (type_is_ancestor(ti, type_interface)) {
  16. assert(ti->instance_size == 0);
  17. assert(ti->abstract);
  18. assert(!ti->instance_init);
  19. assert(!ti->instance_post_init);
  20. assert(!ti->instance_finalize);
  21. assert(!ti->num_interfaces);
  22. }
  23. ti->class = g_malloc0(ti->class_size);
  24. parent = type_get_parent(ti);
  25. if (parent) {
  26. type_initialize(parent);
  27. GSList *e;
  28. int i;
  29. g_assert(parent->class_size <= ti->class_size);
  30. g_assert(parent->instance_size <= ti->instance_size);
  31. memcpy(ti->class, parent->class, parent->class_size);
  32. ti->class->interfaces = NULL;
  33. for (e = parent->class->interfaces; e; e = e->next) {
  34. InterfaceClass *iface = e->data;
  35. ObjectClass *klass = OBJECT_CLASS(iface);
  36. type_initialize_interface(ti, iface->interface_type, klass->type);
  37. }
  38. for (i = 0; i < ti->num_interfaces; i++) {
  39. TypeImpl *t = type_get_by_name(ti->interfaces[i].typename);
  40. if (!t) {
  41. error_report("missing interface '%s' for object '%s'",
  42. ti->interfaces[i].typename, parent->name);
  43. abort();
  44. }
  45. for (e = ti->class->interfaces; e; e = e->next) {
  46. TypeImpl *target_type = OBJECT_CLASS(e->data)->type;
  47. if (type_is_ancestor(target_type, t)) {
  48. break;
  49. }
  50. }
  51. if (e) {
  52. continue;
  53. }
  54. type_initialize_interface(ti, t, t);
  55. }
  56. }
  57. ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
  58. object_property_free);
  59. ti->class->type = ti;
  60. while (parent) {
  61. if (parent->class_base_init) {
  62. parent->class_base_init(ti->class, ti->class_data);
  63. }
  64. parent = type_get_parent(parent);
  65. }
  66. if (ti->class_init) {
  67. ti->class_init(ti->class, ti->class_data);
  68. }
  69. }

在type_initialize函数中(最后一部分),会调用class_init,将纸面上的class也即TypeImpl变为ObjectClass。ObjectClass是所有类的祖先,MachineClass是它的子类。在这里,肯定可以找到之前注册过的TypeImpl,并调用其class_init函数。

因而,pc_machine_##suffix##class_init(本例中是pc_machine_v8_1_class_init)会被调用。

  1. static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
  2. {
  3. MachineClass *mc = MACHINE_CLASS(oc);
  4. pc_i440fx_8_1_machine_options(mc);
  5. mc->init = pc_init_v8_1;
  6. }

在此函数中,pc_i440fx_machine_options才真正由pc_i440fx_8_1machine_options调用,从而初始化MachineClass。

pc_i440fx_8_1machine_options函数在hw/i386/pc_piix.c中,代码如下:

  1. static void pc_i440fx_8_1_machine_options(MachineClass *m)
  2. {
  3. pc_i440fx_machine_options(m);
  4. m->alias = "pc";
  5. m->is_default = true;
  6. }

pc_i440fx_machine_options函数就在上边,代码如下:

  1. static void pc_i440fx_machine_options(MachineClass *m)
  2. {
  3. PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
  4. pcmc->pci_root_uid = 0;
  5. pcmc->default_cpu_version = 1;
  6. m->family = "pc_piix";
  7. m->desc = "Standard PC (i440FX + PIIX, 1996)";
  8. m->default_machine_opts = "firmware=bios-256k.bin";
  9. m->default_display = "std";
  10. m->default_nic = "e1000";
  11. m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL);
  12. machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE);
  13. machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
  14. }

pc_machine_v8_1_class_init函数接下来将MachineClass的init函数设置为pc_init##suffix。

    mc->init = pc_init_v8_1;

因此,在select_machine函数执行完毕后,就有一个MachineClass了。

MachineClass的定义在include/qemu/typedefs.h中,如下:

typedef struct MachineClass MachineClass;

struct MachineClass的定义在include/hw/boards.h中,如下:

  1. struct MachineClass {
  2. /*< private >*/
  3. ObjectClass parent_class;
  4. /*< public >*/
  5. const char *family; /* NULL iff @name identifies a standalone machtype */
  6. char *name;
  7. const char *alias;
  8. const char *desc;
  9. const char *deprecation_reason;
  10. void (*init)(MachineState *state);
  11. void (*reset)(MachineState *state, ShutdownCause reason);
  12. void (*wakeup)(MachineState *state);
  13. int (*kvm_type)(MachineState *machine, const char *arg);
  14. BlockInterfaceType block_default_type;
  15. int units_per_default_bus;
  16. int max_cpus;
  17. int min_cpus;
  18. int default_cpus;
  19. unsigned int no_serial:1,
  20. no_parallel:1,
  21. no_floppy:1,
  22. no_cdrom:1,
  23. no_sdcard:1,
  24. pci_allow_0_address:1,
  25. legacy_fw_cfg_order:1;
  26. bool is_default;
  27. const char *default_machine_opts;
  28. const char *default_boot_order;
  29. const char *default_display;
  30. const char *default_nic;
  31. GPtrArray *compat_props;
  32. const char *hw_version;
  33. ram_addr_t default_ram_size;
  34. const char *default_cpu_type;
  35. bool default_kernel_irqchip_split;
  36. bool option_rom_has_mr;
  37. bool rom_file_has_mr;
  38. int minimum_page_bits;
  39. bool has_hotpluggable_cpus;
  40. bool ignore_memory_transaction_failures;
  41. int numa_mem_align_shift;
  42. const char **valid_cpu_types;
  43. strList *allowed_dynamic_sysbus_devices;
  44. bool auto_enable_numa_with_memhp;
  45. bool auto_enable_numa_with_memdev;
  46. bool ignore_boot_device_suffixes;
  47. bool smbus_no_migration_support;
  48. bool nvdimm_supported;
  49. bool numa_mem_supported;
  50. bool auto_enable_numa;
  51. bool cpu_cluster_has_numa_boundary;
  52. SMPCompatProps smp_props;
  53. const char *default_ram_id;
  54. HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
  55. DeviceState *dev);
  56. bool (*hotplug_allowed)(MachineState *state, DeviceState *dev,
  57. Error **errp);
  58. CpuInstanceProperties (*cpu_index_to_instance_props)(MachineState *machine,
  59. unsigned cpu_index);
  60. const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
  61. int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
  62. ram_addr_t (*fixup_ram_size)(ram_addr_t size);
  63. };

回到qemu_create_machine函数中,

  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函数执行完毕后,即获得了一个MachineClass之后,接下来来到以下代码片段:

    current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class)));

MACHINE函数的代码在include/hw/boards.h中,如下:

  1. static inline G_GNUC_UNUSED MachineState *MACHINE(const void *obj)
  2. {
  3. return OBJECT_CHECK(MachineState, obj, TYPE_MACHINE);
  4. }

OBJECT_CHECK宏展开后的函数代码如下:

  1. static inline G_GNUC_UNUSED MachineState *MACHINE(const void *obj)
  2. {
  3. return ((MachineState*)object_dynamic_cast_assert(OBJECT(obj), ("machine"), __FILE__, __LINE__, __func__));
  4. }

现在要回过头来看qemu_create_machine函数(softmmu/vl.c中)调用MACHINE函数时,传递给它的实参:object_new(object_class_get_name(OBJECT_CLASS(machine_class)))。

现在object_new_with_class以及object_new函数成为了关注焦点。这两个函数都在qom/object.c中,代码分别如下:

  1. Object *object_new(const char *typename)
  2. {
  3. TypeImpl *ti = type_get_by_name(typename);
  4. return object_new_with_type(ti);
  5. }
  1. Object *object_new_with_class(ObjectClass *klass)
  2. {
  3. return object_new_with_type(klass->type);
  4. }

可以看到,甭管是哪一个函数,最终都会调用到object_new_with_type函数。

object_new_with_type函数也在qom/object.c中,代码如下:

  1. static Object *object_new_with_type(Type type)
  2. {
  3. Object *obj;
  4. size_t size, align;
  5. void (*obj_free)(void *);
  6. g_assert(type != NULL);
  7. type_initialize(type);
  8. size = type->instance_size;
  9. align = type->instance_align;
  10. /*
  11. * Do not use qemu_memalign unless required. Depending on the
  12. * implementation, extra alignment implies extra overhead.
  13. */
  14. if (likely(align <= __alignof__(qemu_max_align_t))) {
  15. obj = g_malloc(size);
  16. obj_free = g_free;
  17. } else {
  18. obj = qemu_memalign(align, size);
  19. obj_free = qemu_vfree;
  20. }
  21. object_initialize_with_type(obj, size, type);
  22. obj->free = obj_free;
  23. return obj;
  24. }

回到object_new函数中。TypeImpl的instance_init会被调用,创建一个对象。current_machine就是这个对象,其类型是MachineState。

  1. Object *object_new(const char *typename)
  2. {
  3. TypeImpl *ti = type_get_by_name(typename);
  4. return object_new_with_type(ti);
  5. }

至此,兜兜转转一大圈,相关体系结构的对象才创建完毕。整体流程如下图所示(图片援引 《趣谈Linux系统》50 | 计算虚拟化之CPU(上):如何复用集团的人力资源? ):

欲知后事如何,且看下回分解。

举报

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