RAMBlock
对于一个地址空间,我们有了树状的MemoryRegion,有了一维的FlatView,但是还没有讲真正对应的内存在哪里。
在qemu中,这部分的工作交给了RAMBlock。
虚拟机内存的分配流程
按照惯例,我们来看一眼一台虚拟机是如何获得对应的内存的。这个过程有点长,在这里只列出关键的部分。
pc_memory_init()
memory_region_allocate_system_memory()
allocate_system_memory_nonnuma()
memory_region_init_ram_nomigrate()
memory_region_init_ram_shared_nomigrate()
{
mr->ram = true;
mr->destructor = memory_region_destructor_ram;
mr->ram_block = qemu_ram_alloc(size, share, mr, errp);
}
埋得很深,最终还是找到。主要工作还是和MemoryRegion相关,设置了其中几个关键的成员:
ram: 表示有内存对应
destructor: 释放时的操作
ram_block: 这个就是本节要讲的RAMBlock了
ram_list按照空间大小排序的链表
其实到这里已经没啥好多说的了,RAMBlock数据结构就是描述虚拟机在主机上对应的内存空间的。不过呢,在qemu上还用了一个链表把他们连起来。这说明qemu上可以分配不止一个RAMBlock。而且在链表上,他们是按照空间大小排序的。
这部分可以看ram_block_add()函数的注释。
/* Keep the list sorted from biggest to smallest block. Unlike QTAILQ,
* QLIST (which has an RCU-friendly variant) does not have insertion at
* tail, so save the last element in last_block.
*/
RAMBLOCK_FOREACH(block) {
last_block = block;
if (block->max_length < new_block->max_length) {
break;
}
}
用一张图来让大家增加一些直观印象。
ram_list (RAMList)
+------------------------------+
|dirty_memory[] |
| (unsigned long *) |
+------------------------------+
|blocks |
| QLIST_HEAD |
+------------------------------+
|
| RAMBlock RAMBlock
| +---------------------------+ +---------------------------+
+---> |next | -------------> |next |
| QLIST_ENTRY(RAMBlock) | | QLIST_ENTRY(RAMBlock) |
+---------------------------+ +---------------------------+
|offset | |offset |
|used_length | |used_length |
|max_length | |max_length |
| (ram_addr_t) | | (ram_addr_t) |
+---------------------------+ +---------------------------+
地址对应关系
有了RAMBlock后,其中关键的一点就是GPA(Guest Physical Address)是如何和HVP(Host Virtual Address)的映射关系就建立了。
我们用一张图来解释。
RAMBlock RAMBlock
+---------------------------+ +---------------------------+
|next | -----------------------------> |next |
| QLIST_ENTRY(RAMBlock) | | QLIST_ENTRY(RAMBlock) |
+---------------------------+ +---------------------------+
|offset | |offset |
|used_length | |used_length |
|max_length | |max_length |
| (ram_addr_t) | | (ram_addr_t) |
+---------------------------+ +---------------------------+
|host | virtual address of a ram |host |
| (uint8_t *) | in host (mmap) | (uint8_t *) |
+---------------------------+ +---------------------------+
|mr | |mr |
| (struct MemoryRegion *)| | (struct MemoryRegion *)|
+---------------------------+ +---------------------------+
| |
| |
| |
| struct MemoryRegion | struct MemoryRegion
+-->+------------------------+ +-->+------------------------+
|name | |name |
| (const char *) | | (const char *) |
+------------------------+ +------------------------+
|addr | physical address in guest |addr |
| (hwaddr) | (offset in RAMBlock) | (hwaddr) |
|size | |size |
| (Int128) | | (Int128) |
+------------------------+ +------------------------+
GPA -> HVA 的映射由MemoryRegion->addr到RAMBlock->host完成。
是不是有种明心见性的感觉~
Last updated