接前一篇文章: QEMU源码全解析 —— 内存虚拟化(20)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
QEMU内存分析(一):内存虚拟化关键结构体 - Edver - 博客园
特此致谢!
2. QEMU虚拟机内存初始化
上一回沿着主线走到了memory_map_init函数,重点讲解了其中的系统内存区域system_memory相关代码中的memory_region_init函数,本回解析address_space_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");
}
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_init函数参数对应的实参:
address_space_init(&address_space_memory, system_memory, "memory");
- AddressSpace *as
对应的实参为&address_space_memory。address_space_memory是同文件(softmmu/physmem.c)中定义的全局变量:
AddressSpace address_space_io;
AddressSpace address_space_memory;
- MemoryRegion *root
对应的实参为system_memory。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");
- const char *name
对应的实参就是"memory"。
函数参数弄清楚之后,开始看address_space_init函数代码。
首先调用memory_region_ref函数,代码片段如下:
memory_region_ref(root);
memory_region_ref函数也在softmmu/memory.c中,代码如下:
void memory_region_ref(MemoryRegion *mr)
{
/* MMIO callbacks most likely will access data that belongs
* to the owner, hence the need to ref/unref the owner whenever
* the memory region is in use.
*
* The memory region is a child of its owner. As long as the
* owner doesn't call unparent itself on the memory region,
* ref-ing the owner will also keep the memory region alive.
* Memory regions without an owner are supposed to never go away;
* we do not ref/unref them because it slows down DMA sensibly.
*/
if (mr && mr->owner) {
object_ref(mr->owner);
}
}
函数中的注释已经讲得很清楚了:
MMIO回调很可能会访问属于owner的数据,因此需要在使用内存区域时引用/取消(ref/unref)引用owner。
内存区域是其owner的子区域。只要owner没有在内存区域上调用unparent本身,引用所有者也会使内存区域保持活动状态。
没有own的记忆区域应该永远不会消失;我们不引用/取消引用它们,因为这会明显减慢DMA的速度。
接下来对于AddressSpace *as所指向的&address_space_memory、即address_space_memory的各成员进行初始化。代码片段如下:
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");
再来回顾一下AddressSpace结构的定义,在include/exec/memory.h中,如下:
/**
* struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects
*/
struct AddressSpace {
/* private: */
struct rcu_head rcu;
char *name;
MemoryRegion *root;
/* Accessed via RCU. */
struct FlatView *current_map;
int ioeventfd_nb;
struct MemoryRegionIoeventfd *ioeventfds;
QTAILQ_HEAD(, MemoryListener) listeners;
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
};
- address_space_memory.root为system_memory,这样就把AddressSpace和MemoryRegion串联起来了。
- address_space_memory.current_map为NULL。
- address_space_memory.ioeventfd_nb为0。
- address_space_memory.ioeventfds为NULL。
- address_space_memory.name为"memory"。
余下的成员都是与QTAILQ相关的了,放在下一回解析。