接前一篇文章: QEMU源码全解析 —— 内存虚拟化(22)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
QEMU内存分析(一):内存虚拟化关键结构体 - Edver - 博客园
特此致谢!
2. QEMU虚拟机内存初始化
上一回在讲解address_space_init函数时,讲到了最后两个函数调用。为了便于理解和回顾,再次贴出address_space_init函数代码,在softmmu/memory.c中,如下:
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
{
memory_region_ref(root);
as->root = root;
as->current_map = NULL;
as->ioeventfd_nb = 0;
as->ioeventfds = NULL;
QTAILQ_INIT(&as->listeners);
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
as->name = g_strdup(name ? name : "anonymous");
address_space_update_topology(as);
address_space_update_ioeventfds(as);
}
本回开始解析address_space_update_topology函数。address_space_update_topology函数在中,代码如下:
static void address_space_update_topology(AddressSpace *as)
{
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
flatviews_init();
if (!g_hash_table_lookup(flat_views, physmr)) {
generate_memory_topology(physmr);
}
address_space_set_flatview(as);
}
终于又回到这个函数了。之前一直没有按照自己的节奏理解和讲解,这回终于可以按照自己的风格和节奏进行解析了。
先来看address_space_update_topology函数参数AddressSpace *as对应的实参:
前文已多次提到,这个as实际上指向的就是address_space_memory。
as->root是在address_space_init函数中赋值的,实际上就是system_memory。
那么,就开始第1个函数调用的解析:
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
memory_region_get_flatview_root函数在同文件(softmmu/memory.c)中,代码如下:
static MemoryRegion *memory_region_get_flatview_root(MemoryRegion *mr)
{
while (mr->enabled) {
if (mr->alias) {
if (!mr->alias_offset && int128_ge(mr->size, mr->alias->size)) {
/* The alias is included in its entirety. Use it as
* the "real" root, so that we can share more FlatViews.
*/
mr = mr->alias;
continue;
}
} else if (!mr->terminates) {
unsigned int found = 0;
MemoryRegion *child, *next = NULL;
QTAILQ_FOREACH(child, &mr->subregions, subregions_link) {
if (child->enabled) {
if (++found > 1) {
next = NULL;
break;
}
if (!child->addr && int128_ge(mr->size, child->size)) {
/* A child is included in its entirety. If it's the only
* enabled one, use it in the hope of finding an alias down the
* way. This will also let us share FlatViews.
*/
next = child;
}
}
}
if (found == 0) {
return NULL;
}
if (next) {
mr = next;
continue;
}
}
return mr;
}
return NULL;
}
这里,memory_region_get_flatview_root函数的参数MemoryRegion *mr对应的实参就是system_memory,也就是虚拟机内存地址空间address_space_memory对应的根MemoryRegion system_memory。那么memory_region_get_flatview_root函数的作用正如其名称,得到虚拟机内存地址空间根内部平坦化之后的根。
记下来是第2个函数flatviews_init。
static void address_space_update_topology(AddressSpace *as)
{
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
flatviews_init();
if (!g_hash_table_lookup(flat_views, physmr)) {
generate_memory_topology(physmr);
}
address_space_set_flatview(as);
}
flatviews_init函数也在softmmu/memory.c中,代码如下:
static void flatviews_init(void)
{
static FlatView *empty_view;
if (flat_views) {
return;
}
flat_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify) flatview_unref);
if (!empty_view) {
empty_view = generate_memory_topology(NULL);
/* We keep it alive forever in the global variable. */
flatview_ref(empty_view);
} else {
g_hash_table_replace(flat_views, NULL, empty_view);
flatview_ref(empty_view);
}
}
flatviews_init函数没有带任何参数,可以说与之前的函数并无瓜葛。但是并非是完全不相关,因为有flat_views的存在。flat_views是一个静态全局变量,在同文件(softmmu/memory.c)中,定义如下:
static GHashTable *flat_views;
既然是全局变量,那么其初始值必然为NULL。那么这里在flatviews_init函数中,能够往下走,而不会因为已经创建过,而直接返回。
flatviews_init函数接下来的代码的解析,请看下回。