QEMU内存管理之FlatView模型(QEMU2.0.0)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leoufung/article/details/48781203
在QEMU的内存管理中的FlatView描述了QEMU虚拟机内存平坦展开的情况。

首先看一下FlatView模型
QEMU内存管理之FlatView模型(QEMU2.0.0) - 六六哥 - 六六哥的博客
 

FlatView的原理:
1. 首先FlatView模型是通过FlatView和FlatRange两个对象组成。
2. FlatView是该段内存的整体视图的管理结构,一个FlatView由一组FlatRange组成。
3. 每个FlatRange代表了虚拟机上的一段内存,多个FlagRange就组成了一个内存视图,这些FlatRange在物理地址空间上不一定是相邻的。
3. 每个FlatView代表了某一类内存的组合,用作特殊的用途(如系统内存空间,MMIO内存地址空间),通常一个FlatView同一个特定用途的Address_Space进行关联
4. 每个FlatRange所界定的GUEST物理地址空间范围通过AddrRange所界定,有关AddrRange的内存,请参考《QEMU的AddrRange地址空间对象模型算法总结(QEMU2.0.0)》
5. FlatRange数组在FlatView初始化的时候为0个,也就是没有分配数组。当进行flatview_insert()的操作的时候,才会动态分配出来
6. 为了简化FlatView,通常将地址空间上连续的FlatRange进行合并,合并为1个FlatRange。如图中的r3,r4,r5就可以进行合并的区间,合并后都合并为r3。r4和r5的内容将被后面的FlatRange的数组元素覆盖掉
关于计算地址的方法和其他128bit的算法,请参考《QEMU的128位算法集合(基于QEMU2.0.0)》

了解了上面的原理,相关代码如下

struct FlatRange {
    MemoryRegion *mr;
    hwaddr offset_in_region;
    AddrRange addr;
    uint8_t dirty_log_mask;
    bool romd_mode;
    bool readonly;
};

/*
* FlatView是将树状AS的平行展开,可以想象为一个GUEST内存条,所以FlatView里面都是GPA相关的内容
* FlatView里含有多个FlatRange,每个FlatRange代表了一段内存,
* 所有的FlatRange共同构成了FlatView的Guest内存条
* 每个FlatRange通过AddrRange标记该段GUEST内存的大小和长度 
*/
struct FlatView {
    unsigned ref;               /*引用计数*/
    FlatRange *ranges;          /*指向FlatRange数组*/
    unsigned nr;               /*已经使用了多少个FlatRange*/
    unsigned nr_allocated;     /*总共分配了多少FlatRange*/
};

/*
* 遍历FlatView下所有的FlatRange
*/
#define FOR_EACH_FLAT_RANGE(var, view)          \
    for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)

/*
* 判断两个FlatRange是否代表相同的地址空间
* AddrRange的判断请参见<QEMU的AddrRange地址空间对象模型算法总结(QEMU2.0.0)>
*/
static bool flatrange_equal(FlatRange *a, FlatRange *b)
{
    .....
}

/*
* 初始化一个FlatView,开始的时候,内部含有的0个FlatRange
*/
static void flatview_init(FlatView *view)
{
    view->ref = 1;
    view->ranges = NULL;
    view->nr = 0;
    view->nr_allocated = 0;
}
/*
* 向Flatview的指定位置中插入一个FlatRange
*/
static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range)
{
     /*
     * 如果FlatRange数组占满了,那么新分配已开内存(原来的2倍大小)
     * 注意这里用的是realloc,是扩大内存大小,不改变原来指针的位置,
     */
    if (view->nr == view->nr_allocated) {
        view->nr_allocated = MAX(2 * view->nr, 10);
        view->ranges = g_realloc(view->ranges,
                                    view->nr_allocated * sizeof(*view->ranges));
    }
     /*
     * 将指定位置之后的内存都向后搬移一下,空出pos的位置
     */
    memmove(view->ranges + pos + 1, view->ranges + pos,
            (view->nr - pos) * sizeof(FlatRange));

     /*
     * 将新的FlatRange的内容记录到pos位置,完成插入
     */
    view->ranges[pos] = *range;
     /* 每增加一个Range都会让mr的引用加1,因为FlatRange中mr成员会引用mr*/
    memory_region_ref(range->mr);
     /* 使用数量加一 */
    ++view->nr;
}

/*
* 销毁一个FlatView
*/
static void flatview_destroy(FlatView *view)
{
    int i;

     /* 对MR引用计数减少1 */
    for (i = 0; i < view->nr; i++) {
        memory_region_unref(view->ranges[i].mr);
    }
     
     /*释放FlatRange数组*/
    g_free(view->ranges);

     /*释放FlatView*/
    g_free(view);
}

/*增加FlatView引用计数*/
static void flatview_ref(FlatView *view)
{
    atomic_inc(&view->ref);
}

/*减少FlatView引用计数,减少为0的时候,销毁FlatView*/
static void flatview_unref(FlatView *view)
{
    if (atomic_fetch_dec(&view->ref) == 1) {
        flatview_destroy(view);
    }
}

/*
* 判断r1和r2是否可以合并,可以合并返回true

* 可以合并的条件如下:
* 1. r1.end(GPA) == r2.start(GPA)
* 2. 属于同一个MR
* 3. r1.offset+size == r2.offset(在MR内的偏移量正好可以对接上)
* 4. dirty_log相同,迁移使用
* 5. 读写属性相同
* 6. 读写模式相同
*/
static bool can_merge(FlatRange *r1, FlatRange *r2)
{
    ......
}

/*
* 通过合并相邻的FlatRange,简化FlatView
*/
static void flatview_simplify(FlatView *view)
{
    unsigned i, j;

    i = 0;
     /*遍历所有的FlatRange,进行range的合并*/
    while (i < view->nr) {
        j = i + 1;
          /*
          * 将从i开始之后所有的能合并的内存都合并
          * 1. 所有能合并的都往 i 上做合并
          * 2. j指向可以合并的最后一个range的后一个
          */
        while (j < view->nr
               && can_merge(&view->ranges[j-1], &view->ranges[j])) {
            /*可以合并的话,增加i的大小*/
            int128_addto(&view->ranges[i].addr.size, view->ranges[j].addr.size);
            ++j;
        }
          /* i指向了下一个待合并填充的位置*/
        ++i;
          /* 进行内存搬移,将合并了的内存覆盖掉 */
        memmove(&view->ranges[i], &view->ranges[j],
                (view->nr - j) * sizeof(view->ranges[j]));
          /*修改已用FlatRange的数量,减少已经合并的数量*/
        view->nr -= j - i;
    }
}


参考文章
展开阅读全文

没有更多推荐了,返回首页