接前一篇文章: QEMU源码全解析 —— 内存虚拟化(19)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
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);
}
更多内容请看下回。