在qemu内存模型中,MemoryRegion可以说是最为重要的结构体之一,是qemu实现内存模型的基础,在MemoryRegion中定义了subregions和subregions_link两个成员变量,接下来主要分析一下这两个结构体成员变量的作用。
(一)首先看一下这两个成员变量在MemoryRegion中是如何定义的
struct MemoryRegion {
/*some definiation*/
QTAILQ_HEAD(subregions, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
/*some definiation*/
};
从上述代码中可以发现subregions和subregions_link的定义分别使用了QTAILQ_HEAD 和 QTAILQ_ENTRY两个宏定义。关于QTAILQ_HEAD 和 QTAILQ_ENTRY相关的宏定义,在 TAILQ链表队列详解 这篇文章中进行了介绍。
为了方便阅读,我们将这两个宏定义展开,展开后代码片段如下:
struct MemoryRegion {
/*some definiation*/
struct subregions {
struct MemoryRegion *tqh_first;
struct MemoryRegion * *tqh_last;
} subregions;
struct {
struct MemoryRegion *tqe_next;
struct MemoryRegion * *tqe_prev;
} subregions_link;
/*some definiation*/
};
代码展开后,可以知道subregions 是一个结构体,包含了两个成员变量,一个是指针变量tqh_head,一个是二级指针变量tqh_last。subregions_link同样包含两成员变量,一个指针变量tqe_next和二级指针变量tqe_prev。
(二)接下来重点分析这四个指针变量是如何工作的
假设我们现在有一个MemoryRegion,命名为MemoryRegion-parent,MemoryRegion-parent下面有3个同级的子MemoryRegion,分别命名为MemoryRegion-sub1、MemoryRegion-sub2,、MemoryRegion-sub3。那父MemoryRegion和子MemoryRegion是如何通过上面4个指针串联在一起的呢?直接上图
MemoryRegion的subregions其实是充当一个队列(链表)头的作用,从它的定义QTAILQ_HEAD也可以看处理,用来指向子MemoryRegion链表队列。subregions变量的tqh_first用来指向子MemoryRegion链表队列的第一个结点,tqh_last指向的是子MemoryRegion链表队列的最后一个结点的subregions_link的tqe_next属性(注意tqh_last这边不是指向链表队列的最后后一个结点),至于tqh_last为什么这么指向在 TAILQ链表队列详解 这篇文章中进行了介绍。
MemoryRegion的subregions_link用来链接同一父结点下的所有子结点。tqe_next指向下一个结点,而tqe_prev指向前一个结点的subregions_link.tqe_next属性。
通过subregions和subregions_link两个成员变变量提供的4个指针,可以方便的将MemroyRegion以一种有向无环图(树)的形式组织起来。假设MemoryRegion-sub1下面也有两个子节点,那么整个MemoryRegion形成的图如下所示:
在讲解qemu内存模型的相关文档中,我们通常会看到这么一张图:
但是这张图对subregions这个属性的作用表现的有点不够好,我们对这张图进行简单修改,方便看出subregions和subregions_link的作用。父MemoryRegion通过subregions属性找到子MemoryRegion链表的第一个结点,在自MemoryRegion链表中遍历时是通过subregions_link属性实现的。