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

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

本文内容参考:

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

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

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

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

特此致谢!

2. QEMU虚拟机内存初始化

上一回讲到,内存作为虚拟机的基础部分,其初始化除了在pc_init1函数中进行部分工作外,在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函数还是memory_map_init函数,其底层都直接或间接调用了memory_region_init函数。

5937a62d47444d69afaec26106631dcb.png

6cc3b0956459445aad50593f426238f1.png

本回就来看一下memory_region_init函数的源码,以对于相关的几个数据结构体进行进一步综合掌握。

memory_region_init函数在softmmu\memory.c中,代码如下:

void memory_region_init(MemoryRegion *mr,
                        Object *owner,
                        const char *name,
                        uint64_t size)
{
    object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
    memory_region_do_init(mr, owner, name, size);
}

上一回讲过:在memory_map_init函数中会创建两个AddressSpace:address_space_memory和address_space_io,分别用来表示虚拟机的内存地址空间和I/O地址空间,其对应根MemoryRegion分别是system_memory和system_io。

93fbd7643a0b4f29acb9eda70ec1197b.png

也就是说,两次调用memory_region_init函数,传入的实参分别是system_memory(虚拟机的内存地址空间对应的根MemoryRegion)和system_io(虚拟机的I/O地址空间对应的根MemoryRegion)。

回到memory_region_init函数代码。

void memory_region_init(MemoryRegion *mr,
                        Object *owner,
                        const char *name,
                        uint64_t size)
{
    object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
    memory_region_do_init(mr, owner, name, size);
}

memory_region_init函数一共调用了两个函数,一个一个来看。

1)object_initialize函数

代码片段如下:

    object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);

object_initialize函数在qom\object.c中,代码如下:

void object_initialize(void *data, size_t size, const char *typename)
{
    TypeImpl *type = type_get_by_name(typename);

#ifdef CONFIG_MODULES
    if (!type) {
        int rv = module_load_qom(typename, &error_fatal);
        if (rv > 0) {
            type = type_get_by_name(typename);
        } else {
            error_report("missing object type '%s'", typename);
            exit(1);
        }
    }
#endif
    if (!type) {
        error_report("missing object type '%s'", typename);
        abort();
    }

    object_initialize_with_type(data, size, type);
}

object_initialize函数是与QOM(QEMU Object Model)密切相关的,关于这部分内容会单独放在一个专题中讲解,因此这里不做深入解析。

TYPE_MEMORY_REGION宏在include\exec\memory.h中定义,如下:

#define TYPE_MEMORY_REGION "memory-region"

2)memory_region_do_init函数

代码片段如下:

    memory_region_do_init(mr, owner, name, size);

memory_region_do_init函数也在softmmu\memory.c中,代码如下:

static void memory_region_do_init(MemoryRegion *mr,
                                  Object *owner,
                                  const char *name,
                                  uint64_t size)
{
    mr->size = int128_make64(size);
    if (size == UINT64_MAX) {
        mr->size = int128_2_64();
    }
    mr->name = g_strdup(name);
    mr->owner = owner;
    mr->dev = (DeviceState *) object_dynamic_cast(mr->owner, TYPE_DEVICE);
    mr->ram_block = NULL;

    if (name) {
        char *escaped_name = memory_region_escape_name(name);
        char *name_array = g_strdup_printf("%s[*]", escaped_name);

        if (!owner) {
            owner = container_get(qdev_get_machine(), "/unattached");
        }

        object_property_add_child(owner, name_array, OBJECT(mr));
        object_unref(OBJECT(mr));
        g_free(name_array);
        g_free(escaped_name);
    }
}

更多内容请看下回。