QEMU源码全解析 —— 内存虚拟化(23)

接前一篇文章: QEMU源码全解析 —— 内存虚拟化(22)

本文内容参考:

《趣谈 Linux操作系统 》 —— 刘超, 极客时间

QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

【QEMU系统分析之实例篇(七)】-CSDN博客

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函数接下来的代码的解析,请看下回。