이전 기사에서 계속: QEMU 소스 코드 전체 분석 33 —— Machine (3)
이 문서의 내용에 대한 참조:
" Linux 운영 체제 에 대한 흥미로운 이야기 " —— Liu Chao, Geek Time
" QEMU /KVM" 소스 코드 분석 및 적용 - Li Qiang, 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 함수를 호출하여 QEMU 명령줄 매개변수를 파싱하여 MachineClass를 생성하는 방법 즉, 사용자 지정 방법이고 다른 하나는 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가지 기능에 대한 분석은 다음 장을 참조하십시오.