接前一篇文章: QEMU源码全解析 —— 内存虚拟化(13)
本文内容参考:
《 QEMU /KVM源码解析与应用》 —— 李强,机械工业出版社
浅谈QEMU Memory Region 与 Address Space
QEMU内存分析(一):内存虚拟化关键结构体 - Edver - 博客园
特此致谢!
2. QEMU虚拟机内存初始化
上一回从整体上初步讲解了QEMU内存平坦化的核心函数 —— render_memory_region(),本回对其进行深入解析。为了便于理解和回顾,再次贴出render_memory_region函数代码,在softmmu/memory.c中,如下:
/* Render a memory region into the global view. Ranges in @view obscure
* ranges in @mr.
*/
static void render_memory_region(FlatView *view,
MemoryRegion *mr,
Int128 base,
AddrRange clip,
bool readonly,
bool nonvolatile)
{
MemoryRegion *subregion;
unsigned i;
hwaddr offset_in_region;
Int128 remain;
Int128 now;
FlatRange fr;
AddrRange tmp;
if (!mr->enabled) {
return;
}
int128_addto(&base, int128_make64(mr->addr));
readonly |= mr->readonly;
nonvolatile |= mr->nonvolatile;
tmp = addrrange_make(base, mr->size);
if (!addrrange_intersects(tmp, clip)) {
return;
}
clip = addrrange_intersection(tmp, clip);
if (mr->alias) {
int128_subfrom(&base, int128_make64(mr->alias->addr));
int128_subfrom(&base, int128_make64(mr->alias_offset));
render_memory_region(view, mr->alias, base, clip,
readonly, nonvolatile);
return;
}
/* Render subregions in priority order. */
QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
render_memory_region(view, subregion, base, clip,
readonly, nonvolatile);
}
if (!mr->terminates) {
return;
}
offset_in_region = int128_get64(int128_sub(clip.start, base));
base = clip.start;
remain = clip.size;
fr.mr = mr;
fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
fr.romd_mode = mr->romd_mode;
fr.readonly = readonly;
fr.nonvolatile = nonvolatile;
/* Render the region itself into any gaps left by the current view. */
for (i = 0; i < view->nr && int128_nz(remain); ++i) {
if (int128_ge(base, addrrange_end(view->ranges[i].addr))) {
continue;
}
if (int128_lt(base, view->ranges[i].addr.start)) {
now = int128_min(remain,
int128_sub(view->ranges[i].addr.start, base));
fr.offset_in_region = offset_in_region;
fr.addr = addrrange_make(base, now);
flatview_insert(view, i, &fr);
++i;
int128_addto(&base, now);
offset_in_region += int128_get64(now);
int128_subfrom(&remain, now);
}
now = int128_sub(int128_min(int128_add(base, remain),
addrrange_end(view->ranges[i].addr)),
base);
int128_addto(&base, now);
offset_in_region += int128_get64(now);
int128_subfrom(&remain, now);
}
if (int128_nz(remain)) {
fr.offset_in_region = offset_in_region;
fr.addr = addrrange_make(base, remain);
flatview_insert(view, i, &fr);
}
}
render_memory_region函数的本质作用就是将一个MemoryRegion转化成若干个FlatRange,然后插入到第一个参数FlatView的FlatRange成员中。
render_memory_region有6个参数:
1)FlatView *view
第1个参数表示的是一个全局FlatView,就是一个AddressSpace表示的FlatView。
2)MemoryRegion *mr
第2个参数是需要展开的MemoryRegion。
3)Int128 base
第3个参数是base值,表示的是即将展开的MemoryRegion的起始位置。
4)AddrRange clip
第4个参数表示的是一段虚拟机物理地址范围。
5)bool readonly
第5个参数表示MemoryRegion是否可读。
6)bool nonvolatile
第6个参数表示相应的FlatRange是相应的否是非易失的。
render_memory_region函数会递归调用自己,代码片段如下:
/* Render subregions in priority order. */
QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
render_memory_region(view, subregion, base, clip,
readonly, nonvolatile);
}
每次传递子region(subregion)作为第2个参数,并且将展开的FlatRange放到第1个参数FlatView的ranges数组成员中。每次render_memory_region函数返回,都表示一段MemoryRegion展开完毕。当最上层的render_memory_region函数返回时,整个AddressSpace的根MemoryRegion展开完毕。
这样讲起来太过抽象、不容易理解,下一回结合实例对上述机制进行详细讲解。