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

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

本文内容参考:

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

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

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

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

QEMU内存分析(一):内存虚拟化关键结构体 - Edver - 博客园

特此致谢!

2. QEMU虚拟机内存初始化

本回继续沿着上一回的主线继续往前走。

main()

---> qemu_init()

---> qemu_create_machine()

---> cpu_exec_init_all()

---> memory_map_init()

---> address_space_init()

---> address_space_update_topology()

上一回讲到了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函数中,对于系统内存区域system_memory和I/O内存区域system_io,都进行了初始化,并且关联到了相应的地址空间AddressSpace。也可以这样理解,memory_map_init函数中创建了两个AddressSpace:address_space_memory和address_space_io。这两个AddressSpace分别用来表示虚拟机的内存地址空间和I/O地址空间,其对应的根MemoryRegion分别是system_memory和system_io。

这两个Addresspace和对应的根MemoryRegion均为全局变量,在系统中会被很多地方使用。

address_space_memory和address_space_io在同文件(softmmu/physmem.c)中定义,如下:

AddressSpace address_space_io;
AddressSpace address_space_memory;

system_memory和system_io是静态全局变量,也在同文件中定义,如下:

static MemoryRegion *system_memory;
static MemoryRegion *system_io;

这里重点来看system_memory相关的内容。

    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");

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_region_init函数先是调用object_region_init函数,对于前一步创建(分配)的system_memory进行初始化。

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函数的参数对应的实参分别如下:

  • void *data:对应实参就是system_memory;
  • size_t size:对应的实参为sizeof(*mr),即MemoryRegion结构的大小;
  • const char *typename:对应的实参为TYPE_MEMORY_REGION,即"memory-region"。

回到memory_region_init函数中,接下来调用memory_region_do_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);
}

其也在softmmu/memory.c中(就在memory_region_init函数上边),代码如下:

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);
    }
}

这里要讲一下int128_make64函数,它是一个静态内联函数,在include/qemu/int128.h中,代码如下:

static inline Int128 int128_make64(uint64_t a)
{
    return (Int128) { .lo = a, .hi = 0 };
}

Int128的定义就在上边,如下:

typedef struct Int128 Int128;

/*
 * We guarantee that the in-memory byte representation of an
 * Int128 is that of a host-endian-order 128-bit integer
 * (whether using this struct or the __int128_t version of the type).
 * Some code using this type relies on this (eg when copying it into
 * guest memory or a gdb protocol buffer, or by using Int128 in
 * a union with other integer types).
 */
struct Int128 {
#if HOST_BIG_ENDIAN
    int64_t hi;
    uint64_t lo;
#else
    uint64_t lo;
    int64_t hi;
#endif
};

int128_2_64函数也在include/qemu/int128.h中,代码如下:

static inline Int128 int128_2_64(void)
{
    return int128_make128(0, 1);
}

更多内容请看下回。