接前一篇文章: QEMU源码全解析 —— 内存虚拟化(5)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
特此致谢!
2. QEMU虚拟机内存初始化
上一回讲到,内存作为虚拟机的基础部分,其初始化也是在pc_init1函数中进行的,讲解了pc_init1函数中的高低端内存的划分代码。除此之外,main函数中(老版本QEMU代码是在main函数中,新版本是在qemu_create_machine函数中)会调用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函数中有两个函数的调用与内存相关,从名称上也可以看出来,是io_mem_init()和memory_map_init()。一个一个来看。
- io_mem_init函数
io_mem_init函数在softmmu/physmem.c中,代码如下:
static void io_mem_init(void)
{
memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
NULL, UINT64_MAX);
}
memory_region_init_io函数在softmmu/memory.c中,代码如下:
void memory_region_init_io(MemoryRegion *mr,
Object *owner,
const MemoryRegionOps *ops,
void *opaque,
const char *name,
uint64_t size)
{
memory_region_init(mr, owner, name, size);
mr->ops = ops ? ops : &unassigned_mem_ops;
mr->opaque = opaque;
mr->terminates = true;
}
io_mem_init函数比较简单,其功能就是创建若干个包含所有地址空间的MemoryRegion,如io_mem_rom和io_mem_unassigned。更直观地说,该函数完成 I/O 存储器的初始化,生成一个全系统统一的访问存储区域。
- 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函数是一个重要函数,其对系统内存地址空间和I/O地址空间做映射,为后续设备访问做准备。memory_map_init函数中也调用了memory_region_init函数(当然也有memory_region_init_io函数),只不过io_mem_init函数中(间接)调用的是:
memory_region_init(&io_mem_unassigned, NULL, NULL, UINT64_MAX);
而memory_map_init函数中调用的是:
memory_region_init(system_memory, NULL, "system", UINT64_MAX);
在memory_map_init函数中会创建两个AddressSpace:address_space_memory和address_space_io,分别用来表示虚拟机的内存地址空间和I/O地址空间,其对应根MemoryRegion分别是system_memory和system_io。代码片段如下:
也就是说,在memory_map_init函数中,对于系统内存区域system_memory和用于I/O的内存区域system_io,都进行了初始化,并且关联到了相应的地址空间AddressSpace。
这两个AddressSpace(address_space_memory和address_space_io)和对应的根MemoryRegion均为全局变量,在系统中会被很多地方使用。
address_space_memory、address_space_io以及system_memory、system_io均在softmmu/physmem.c中定义,代码如下:
static MemoryRegion *system_memory;
static MemoryRegion *system_io;
AddressSpace address_space_io;
AddressSpace address_space_memory;
本回就到这里,更多内容请看下回。