QEMU源码全解析35 —— Machine(5)

88 篇文章 19 订阅
本文深入解析QEMU源码中与Machine相关的3个关键函数:object_class_get_list_tramp、object_class_foreach_tramp和type_table_get。通过分析这些函数,阐述它们在类型初始化和MachineClass构建过程中的作用,帮助读者理解QEMU内部机制。
摘要由CSDN通过智能技术生成

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

本文内容参考:

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

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

特此致谢!

上回书说到有3个函数需要弄清楚:(1)object_class_get_list_tramp;(2)object_class_foreach_tramp;(3)type_table_get。本回对于这3个函数进行解析。

为了便于理解,再次贴出qom/object.c中的object_class_get_list函数以及其调用的同文件中的object_class_foreach函数,代码分别如下:

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

先来看第1个函数type_table_get,也在qom/object.c中,代码如下:

  1. static GHashTable *type_table_get(void)
  2. {
  3. static GHashTable *type_table;
  4. if (type_table == NULL) {
  5. type_table = g_hash_table_new(g_str_hash, g_str_equal);
  6. }
  7. return type_table;
  8. }

在全局表type_table_get()即返回的type_table中,对于每一项TypeImpl,都执行object_class_foreach_tramp。

  • object_class_foreach_tramp

再来看第2个函数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函数中,OCFData *data被赋值为函数的第3个参数gpointer opaque,该参数实际指向了object_class_foreach函数中的data,即

    OCFData data = { fn, implements_type, include_abstract, opaque }

代入上下文的实际值,最终得到:OCFData data = { fn, TYPE_MACHINE, false, &list }。详见前文书讲到的softmmu/vl.c中的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. ……
  6. return machine_class;
  7. }

我们在前文书讲QOM的时候详细解析过type_initialize函数,其作用是类的初始化,这里当然是对TYPE_MACHINE即"machine"类的初始化。type_initialize函数中会调用class_init函数将纸面上的class即TypeImpl转变为ObjectClass。前文书也提到过,ObjectClass是所有Class类的祖先,而这里的MachineClass是其子类。

object_class_foreach_tramp函数最后的

    data->fn(k, data->opaque);

代入实际值为object_class_get_list_tramp(type->class, &list)。

  • object_class_get_list_tramp

最后看第3个函数object_class_get_list_tramp,其也在qom/object.c中,代码如下:

  1. static void object_class_get_list_tramp(ObjectClass *klass, void *opaque)
  2. {
  3. GSList **list = opaque;
  4. *list = g_slist_prepend(*list, klass);
  5. }

在命令行中传入"-machine xx-xxx-xxx"也好,不指定相关值而使用默认值也罢,最终都能够找到之前已注册过的TypeImpl,并调用它的class_init函数。因而pc_machine_##suffix##class_init即“.class_init = pc_machine_v7_1_class_init”会被调用。再次给出相关代码,如下:

  1. static void pc_init_v7_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_v7_1_class_init(ObjectClass *oc, void *data)
  11. {
  12. MachineClass *mc = MACHINE_CLASS(oc);
  13. pc_i440fx_7_1_machine_options(mc);
  14. mc->init = pc_init_v7_1;
  15. }
  16. static const TypeInfo pc_machine_type_v7_1 = {
  17. .name = "pc-i440fx-7.1" TYPE_MACHINE_SUFFIX,
  18. .parent = TYPE_PC_MACHINE,
  19. .class_init = pc_machine_v7_1_class_init,
  20. };
  21. static void pc_machine_init_v7_1(void)
  22. {
  23. type_register(&pc_machine_type_v7_1);
  24. }

在pc_machine_##suffix##class_init即pc_machine_v7_1_class_init函数中,pc_i440fx_7_1_machine_options函数才真正被调用以初始化MachineClass,并将MachineClass的init函数设置为pc_init##suffix即pc_init_v7_1。也即,当select_machine执行完毕后,就有一个MachineClass了。

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

  1. static void pc_i440fx_7_1_machine_options(MachineClass *m)
  2. {
  3. PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
  4. pc_i440fx_machine_options(m);
  5. m->alias = "pc";
  6. m->is_default = true;
  7. pcmc->default_cpu_version = 1;
  8. pcmc->legacy_no_rng_seed = true;
  9. }

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

  1. static void pc_i440fx_machine_options(MachineClass *m)
  2. {
  3. PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
  4. pcmc->default_nic_model = "e1000";
  5. pcmc->pci_root_uid = 0;
  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. machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE);
  11. machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
  12. }

本回内容较多,需要认真结合前文反复理解,也算作一个对前文的复习回顾。介绍完了这3个函数后,回到select_machine函数中,继续往下进行解析。详情请看下文。

举报

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