在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个指针串联在一起的呢?直接上图

如何理解memory调试数据_指针变量


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形成的图如下所示:

如何理解memory调试数据_链表_02


在讲解qemu内存模型的相关文档中,我们通常会看到这么一张图:

如何理解memory调试数据_如何理解memory调试数据_03


但是这张图对subregions这个属性的作用表现的有点不够好,我们对这张图进行简单修改,方便看出subregions和subregions_link的作用。父MemoryRegion通过subregions属性找到子MemoryRegion链表的第一个结点,在自MemoryRegion链表中遍历时是通过subregions_link属性实现的。

如何理解memory调试数据_如何理解memory调试数据_04