Продолжение предыдущей статьи: Полный анализ исходного кода QEMU 33 —— Машина (3)
Ссылки на содержание этой статьи:
«Интересный разговор об операционной системе Linux » — Лю Чао, Geek Time
Анализ и применение исходного кода QEMU / KVM — Ли Цян, Machinery Industry Press
Большое спасибо!
Макрос DEFINE_I440FX_MACHINE подробно разбирался в прошлый раз, в этот раз я расскажу о машинных частях инициализации QEMU.
В старой версии QEMU функция select_machine будет вызываться в основной функции main, а новая версия помещает main() -> qemu_main() -> qemu_init() -> qemu_create_machine -> select_machine().
Начнем с функции qemu_create_machine. Функция 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);
}
}
static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
GlobalProperty *g;
g = g_malloc0(sizeof(*g));
g->driver = qemu_opt_get(opts, "driver");
g->property = qemu_opt_get(opts, "property");
g->value = qemu_opt_get(opts, "value");
qdev_prop_register_global(g);
return 0;
}
Первая строка кода — это функция select_machine, а фрагмент кода выглядит следующим образом:
MachineClass *machine_class = select_machine(qdict, &error_fatal);
Как следует из названия, функция select_machine предназначена для выбора MachineClass, который может быть указан пользователем, и если пользователь его не укажет, будет использоваться системное значение по умолчанию. Если это последнее, тип машины, соответствующий номеру последней версии QEMU, является настройкой по умолчанию. Поскольку исходный код автора — qemu-7.1.0, тип машины по умолчанию — pc-i440fx-7.1-machine.
Функция select_machine также находится в softmmu/vl.c, ее код выглядит следующим образом:
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 есть два способа генерации MachineClass: один способ — вызвать функцию find_machine для генерации MachineClass путем разбора параметров командной строки QEMU, то есть указанным пользователем способом; другой способ заключается в том, чтобы найти его с помощью функции find_default_machine. Класс MachineClass по умолчанию является системным методом по умолчанию. Две функции этих двух методов будут проанализированы отдельно позже.
Перед этим стоит покопаться в функции: object_class_get_list. Независимо от того, указан ли он пользователем или методом системы по умолчанию, необходимо сначала вызвать функцию object_class_get_list для получения списка MachineClass, а затем выполнить его поиск. Фрагмент кода выглядит следующим образом:
GSList *machines = object_class_get_list(TYPE_MACHINE, false);
Функция object_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 видно, что первый параметр — это указатель на функцию, который указывает на входящий фактический параметр object_class_get_list_tramp.
Теперь осталось выяснить 4 функции: (1) object_class_get_list_tramp; (2) object_foreach_tramp; (3) type_table_get; (4) g_hash_table_foreach.
Для анализа этих 4 функций см. следующую главу.