接前一篇文章: QEMU源码全解析 —— 内存虚拟化(18)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
QEMU内存分析(一):内存虚拟化关键结构体 - Edver - 博客园
特此致谢!
2. QEMU虚拟机内存初始化
前边用了几回重点讲解了内存平坦化。按道理来说,接下来该往下继续讲了。但是,笔者在此并不想开新的内容,因为这几回实际上大都是参考了《QEMU/KVM源码解析与应用》,其中的内容笔者还是觉得不够细,没有完全涉及到路径中遇到的每一个函数,自己也还没有完全吃透。在此,笔者准备再次重走以下流程,把整个流程中涉及到的各个函数(最起码绝大多数函数)、尤其是之前没有展开讲的函数解析一遍,把这一流程用“磨蔓儿”的方式进行讲解,以使自己以及读者加深印象,一次就把这部分内容彻底掌握。
main()
---> qemu_init()
---> qemu_create_machine()
---> cpu_exec_init_all()
---> memory_map_init()
---> address_space_init()
---> address_space_update_topology()
(1)main函数
QEMU的入口main函数在softmmu/main.c中,代码如下:
int main(int argc, char **argv)
{
qemu_init(argc, argv);
return qemu_main();
}
(2)qemu_init函数
qemu_init函数在softmmu/vl.c中,函数很长,一共有将近1000行(qemu-8.1.4版本),这里只截取调用qemu_create_machine函数的一段:
static QDict *machine_opts_dict;
……
void qemu_init(int argc, char **argv)
{
……
machine_opts_dict = qdict_new();
if (userconfig) {
qemu_read_default_config_file(&error_fatal);
}
/* second pass of option parsing */
optind = 1;
for(;;) {
if (optind >= argc)
break;
if (argv[optind][0] != '-') {
loc_set_cmdline(argv, optind, 1);
drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
} else {
const QEMUOption *popt;
popt = lookup_opt(argc, argv, &optarg, &optind);
if (!(popt->arch_mask & arch_type)) {
error_report("Option not supported for this target");
exit(1);
}
switch(popt->index) {
case QEMU_OPTION_cpu:
/* hw initialization will check this */
cpu_option = optarg;
break;
……
case QEMU_OPTION_nographic:
qdict_put_str(machine_opts_dict, "graphics", "off");
nographic = true;
dpy.type = DISPLAY_TYPE_NONE;
break;
……
case QEMU_OPTION_kernel:
qdict_put_str(machine_opts_dict, "kernel", optarg);
break;
case QEMU_OPTION_initrd:
qdict_put_str(machine_opts_dict, "initrd", optarg);
break;
case QEMU_OPTION_append:
qdict_put_str(machine_opts_dict, "append", optarg);
break;
case QEMU_OPTION_dtb:
qdict_put_str(machine_opts_dict, "dtb", optarg);
break;
……
case QEMU_OPTION_bios:
qdict_put_str(machine_opts_dict, "firmware", optarg);
break;
……
case QEMU_OPTION_enable_kvm:
qdict_put_str(machine_opts_dict, "accel", "kvm");
break;
case QEMU_OPTION_M:
case QEMU_OPTION_machine:
{
bool help;
keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);
if (help) {
machine_help_func(machine_opts_dict);
exit(EXIT_SUCCESS);
}
break;
}
……
case QEMU_OPTION_usb:
qdict_put_str(machine_opts_dict, "usb", "on");
break;
case QEMU_OPTION_usbdevice:
qdict_put_str(machine_opts_dict, "usb", "on");
add_device_config(DEV_USB, optarg);
break;
……
case QEMU_OPTION_no_acpi:
warn_report("-no-acpi is deprecated, use '-machine acpi=off' instead");
qdict_put_str(machine_opts_dict, "acpi", "off");
break;
case QEMU_OPTION_no_hpet:
warn_report("-no-hpet is deprecated, use '-machine hpet=off' instead");
qdict_put_str(machine_opts_dict, "hpet", "off");
break;
……
}
……
qemu_create_machine(machine_opts_dict);
……
}
(3)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);
}
}
(4)cpu_exec_init_all函数
cpu_exec_init_all函数在softmmu/physmem.c中,代码如下:
void cpu_exec_init_all(void)
{
qemu_mutex_init(&ram_list.mutex);
/* The data structures we set up here depend on knowing the page size,
* so no more changes can be made after this point.
* In an ideal world, nothing we did before we had finished the
* machine setup would care about the target page size, and we could
* do this much later, rather than requiring board models to state
* up front what their requirements are.
*/
finalize_target_page_bits();
io_mem_init();
memory_map_init();
qemu_mutex_init(&map_client_list_lock);
}
从cpu_exec_init_all函数开始,就步入正题了。可以看到,cpu_exec_init_all函数并无参数,因此它与之前的qemu_create_machine等函数实际上并无过多瓜葛,只是调用与被调用的关系。
前文书早已讲过,cpu_exec_init_all函数会进行一些初始化工作,其中io_mem_init()和memory_map_init()两个函数的调用与内存相关。其中,io_mem_init()用于对I/O内存区域进行初始化;而memory_map_init()则是对系统内存区域进行初始化。这里重点关注memory_map_init函数。
(5)memory_map_init函数
memory_map_init函数也在softmmu/physmem.c中,代码如下:
static void memory_map_init(void)
{
system_memory = g_malloc(sizeof(*system_memory));
memory_region_init(system_memory, NULL, "system", UINT64_MAX);
address_space_init(&address_space_memory, system_memory, "memory");
system_io = g_malloc(sizeof(*system_io));
memory_region_init_io(system_io, NULL, &unassigned_io_ops, NULL, "io",
65536);
address_space_init(&address_space_io, system_io, "I/O");
}
对于memory_map_init函数再一次的深入解析,请看下回。