接前一篇文章: QEMU源码全解析 —— 内存虚拟化(10)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
特此致谢!
2. QEMU虚拟机内存初始化
上一回开始对于之前没有仔细讲过的知识进行精讲,首先是虚拟机内存平坦化,即地址空间(AddressSpace)中与树形结构(MemoryRegion)并列的“平面”结构(FlatView)两者之间的转换。
先再来回顾一下几个结构体的定义。
- AddressSpace
AddressSpace的定义在include/qemu/typedefs.h中,如下:
typedef struct AddressSpace AddressSpace;
struct Address的定义在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;
};
AddressSpace结构用来表示一个虚拟机或者虚拟CPU能够访问的所有物理地址。
AddressSpace中同时包含了两个QEMU内存管理框架的核心结构:MemoryRegion和FlatView。这两者配合使用,MemoryRegion是基础,用于正向管理;Flatview用于反向查找。
- MemoryRegion
MemoryRegion的定义在include/exec/memory.h中,如下:
#define TYPE_MEMORY_REGION "memory-region"
DECLARE_INSTANCE_CHECKER(MemoryRegion, MEMORY_REGION,
TYPE_MEMORY_REGION)
在此不详细展开宏,而直接给出struct MemoryRegion定义,也在include/exec/memory.h中,代码如下:
/** MemoryRegion:
*
* A struct representing a memory region.
*/
struct MemoryRegion {
Object parent_obj;
/* private: */
/* The following fields should fit in a cache line */
bool romd_mode;
bool ram;
bool subpage;
bool readonly; /* For RAM regions */
bool nonvolatile;
bool rom_device;
bool flush_coalesced_mmio;
uint8_t dirty_log_mask;
bool is_iommu;
RAMBlock *ram_block;
Object *owner;
/* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */
DeviceState *dev;
const MemoryRegionOps *ops;
void *opaque;
MemoryRegion *container;
int mapped_via_alias; /* Mapped via an alias, container might be NULL */
Int128 size;
hwaddr addr;
void (*destructor)(MemoryRegion *mr);
uint64_t align;
bool terminates;
bool ram_device;
bool enabled;
bool warning_printed; /* For reservations */
uint8_t vga_logging_count;
MemoryRegion *alias;
hwaddr alias_offset;
int32_t priority;
QTAILQ_HEAD(, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
QTAILQ_HEAD(, CoalescedMemoryRange) coalesced;
const char *name;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
RamDiscardManager *rdm; /* Only for RAM */
/* For devices designed to perform re-entrant IO into their own IO MRs */
bool disable_reentrancy_guard;
};
MemoryRegion表示的是虚拟机中的一段内存区域,它是内存模拟中的核心结构,整个内存的模拟都是通过MemoryRegion构成的无环图完成的。图的叶子节点是实际分配给虚拟机的物理内存或者MMIO,中间节点则表示内存总线,内存控制器是其它MemoryRegion的别名。
- FlatView
FlatView的定义也在include/exec/memory.h中(就在struct Address定义的下边),如下:
/* Flattened global view of current active memory hierarchy. Kept in sorted
* order.
*/
struct FlatView {
struct rcu_head rcu;
unsigned ref;
FlatRange *ranges;
unsigned nr;
unsigned nr_allocated;
struct AddressSpaceDispatch *dispatch;
MemoryRegion *root;
};
FlatView结构就是把树形内存结构变成平的内存结构。因为树形内存结构比较容易管理,而平的内存结构比较方便与内核通信以请求物理内存。虽然操作系统内核里边实际上也是用树形结构来表示内存区域的,但是在用户态向内核申请内存的时候,会按照平的、连续的模式进行申请。那么,由于QEMU也是在用户态,它也得按照平的、连续的模式进行申请,所以就要做这样一个转换。
虚拟机内存的平坦化以AddressSpace为单位,也就是以AddressSpace的根MemoryRegion为起点,将其所表示的内存拓扑的无环图结构变为平坦模式。具体的实现函数是address_space_update_topology和其中的generate_memory_topology,两个均在softmmu/memory.c中,代码如下:
static void address_space_update_topology(AddressSpace *as)
{
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
flatviews_init();
if (!g_hash_table_lookup(flat_views, physmr)) {
generate_memory_topology(physmr);
}
address_space_set_flatview(as);
}
/* Render a memory topology into a list of disjoint absolute ranges. */
static FlatView *generate_memory_topology(MemoryRegion *mr)
{
int i;
FlatView *view;
view = flatview_new(mr);
if (mr) {
render_memory_region(view, mr, int128_zero(),
addrrange_make(int128_zero(), int128_2_64()),
false, false);
}
flatview_simplify(view);
view->dispatch = address_space_dispatch_new(view);
for (i = 0; i < view->nr; i++) {
MemoryRegionSection mrs =
section_from_flat_range(&view->ranges[i], view);
flatview_add_to_dispatch(view, &mrs);
}
address_space_dispatch_compact(view->dispatch);
g_hash_table_replace(flat_views, mr, view);
return view;
}
从代码层面概括来讲,虚拟机内存的平坦化过程是将AddressSpace中的根MemoryRegion表示的虚拟机内存地址空间转变成一个平坦的线性地址空间。每一段线性地址空间的属性和其所属的MemoryRegion都一致,每一段线性空间与虚拟机的物理地址空间都相互关联。
虚拟机内存的平坦化 以AddressSpace为单位 ,也就是 以AddressSpace的根MemoryRegion为起点 ,将其所表示的内存拓扑的无环图变成平坦模式。
更多内容请看下回。