接前一篇文章: QEMU源码全解析34 —— Machine(4)
本文内容参考:
《 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函数,代码分别如下:
- 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;
- }
- 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;
- }
- type_table_get
先来看第1个函数type_table_get,也在qom/object.c中,代码如下:
- static GHashTable *type_table_get(void)
- {
- static GHashTable *type_table;
-
- if (type_table == NULL) {
- type_table = g_hash_table_new(g_str_hash, g_str_equal);
- }
-
- return type_table;
- }
在全局表type_table_get()即返回的type_table中,对于每一项TypeImpl,都执行object_class_foreach_tramp。
- object_class_foreach_tramp
再来看第2个函数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函数中,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函数:
- 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);
- ……
- return machine_class;
- }
我们在前文书讲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中,代码如下:
- static void object_class_get_list_tramp(ObjectClass *klass, void *opaque)
- {
- GSList **list = opaque;
-
- *list = g_slist_prepend(*list, klass);
- }
在命令行中传入"-machine xx-xxx-xxx"也好,不指定相关值而使用默认值也罢,最终都能够找到之前已注册过的TypeImpl,并调用它的class_init函数。因而pc_machine_##suffix##class_init即“.class_init = pc_machine_v7_1_class_init”会被调用。再次给出相关代码,如下:
- static void pc_init_v7_1(MachineState *machine)
- {
- void (*compat)(MachineState *m) = (NULL);
- if (compat) {
- compat(machine);
- }
- pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
- TYPE_I440FX_PCI_DEVICE);
- }
-
- static void pc_machine_v7_1_class_init(ObjectClass *oc, void *data)
- {
- MachineClass *mc = MACHINE_CLASS(oc);
- pc_i440fx_7_1_machine_options(mc);
- mc->init = pc_init_v7_1;
- }
- static const TypeInfo pc_machine_type_v7_1 = {
- .name = "pc-i440fx-7.1" TYPE_MACHINE_SUFFIX,
- .parent = TYPE_PC_MACHINE,
- .class_init = pc_machine_v7_1_class_init,
- };
- static void pc_machine_init_v7_1(void)
- {
- type_register(&pc_machine_type_v7_1);
- }
在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中,代码如下:
- static void pc_i440fx_7_1_machine_options(MachineClass *m)
- {
- PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
- pc_i440fx_machine_options(m);
- m->alias = "pc";
- m->is_default = true;
- pcmc->default_cpu_version = 1;
- pcmc->legacy_no_rng_seed = true;
- }
pc_i440fx_machine_options函数就在上边,代码如下:
- static void pc_i440fx_machine_options(MachineClass *m)
- {
- PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
- pcmc->default_nic_model = "e1000";
- pcmc->pci_root_uid = 0;
-
- m->family = "pc_piix";
- m->desc = "Standard PC (i440FX + PIIX, 1996)";
- m->default_machine_opts = "firmware=bios-256k.bin";
- m->default_display = "std";
- machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE);
- machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
- }
本回内容较多,需要认真结合前文反复理解,也算作一个对前文的复习回顾。介绍完了这3个函数后,回到select_machine函数中,继续往下进行解析。详情请看下文。