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

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

本文内容参考:

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

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

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

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

特此致谢!

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。代码片段如下:

ffc17cd2106b4ca7824bf9ad0b5adcda.png

也就是说,在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;

本回就到这里,更多内容请看下回。