MD中bit地图源代码分析-数据结构

MD中bitmap源代码分析--数据结构

本篇分析bitmap的数据结构的设计,并基于此分析bitmap的工作机制。

  为了后面更清楚的理解,先有个总体印象,给出整体的结构图:

 MD中bit地图源代码分析-数据结构

  在下面的描述中涉及到的内容可以对照到上图中相应部分,便于理解。

  首先,我们从宏观的角度来分析整体结构。bitmap file存在于磁盘,内部存放着很多个bit,每个bit对应于磁盘数据中的一个chunk。在内存空间中也存在一个区域存放bitmap file缓存,与磁盘bitmap file的每个bit一一对应。内存空间中还存在一个区域存放filemap_attr,用来管理bitmap file缓存中每个page的页属性。内存空间中还存在一个区域存放和管理*bmc,用来管理对应bitmap file中的bit是否置位和未完成的最大请求数。而这些内存区域的操作都由bitmap这个大结构体关联起来。如下图所示。

 MD中bit地图源代码分析-数据结构

  1. bitmap的结构体有比较多的字段,这里关注几个重要的字段。

    struct bitmap {

      struct bitmap_page *bp; /*指向内存bitmap页的结构*/

      ……

      unsigned long chunks; /*阵列总的chunk数*/

      ……

      struct file *file; /*bitmap文件*/

      ……

      struct page **filemap; /*bitmap文件的缓存页*/

      unsigned long *filemap_attr; /*bitmap文件缓存页的属性*/

      unsigned long file_pages; /*  1、初始化时当做page号累加,

                     2、初始化完成之后,为bitmap file中的page数

                    */

       ……

    };

   这里filemap是指向一系列page结构缓存页的指针构成的数组,所以是page**类型。其在内存中的结构和与bitmap file缓存的关系如下图所示。

MD中bit地图源代码分析-数据结构 

  2. 其中struct bitmap_page结构如下:

    struct bitmap_page {

      char *map/*指向实际分配的内存页*/

      unsigned int hijacked:1; /* 表示是否被劫持。

                    置1的时候说明,内存空间不够的时候使用,

                    这时直接将map指针作为两个*bmc

                   */

      unsigned int count:31; /*该页上有多少脏的chunk */

    };

     bp的整体结构如下如所示:

MD中bit地图源代码分析-数据结构 

  在正常情况下(内存充足),bitmap的bp字段指向一片内存区域,该内存区域就是逐一存放的bitmap_page结构体,也就是结构体数组。而每个这样一个结构体中存在一个map指针,在需要的时候就会在内存中开辟一个page的空间,用来存放逐一存放*bmc。这里map指针指向的page是动态分配的,在需要的时候才会分配page并设置使用相应的*bmc。

  对于bp指针指向的bitmap_page结构,内部分为3个字段。其中的hijacked字段大多数情况下都是置为0的,这也就是内存空间足够分配page的正常情况。这种情况下一个*bmc管理1个bitmap file缓存中的1个bit,一个*map指针管理一个page,而一个page可以存放2048个*bmc(一个*bmc为16位,后面会介绍到),也就是一个map指针管理2048个bitmap file缓存中的bit,count用来作为该map指针管理下的这2048个*bmc对应有多少脏的chunk计数。如下图所示。

MD中bit地图源代码分析-数据结构 

  hijacked字段置为1的情况十分少见,只有在内存空间不够分配page的时候才会将hijacked字段置为1。在这种情况内存不足,分配不了page空间,那么就退而求其次,将*bmc的管理粒度变大,具体方法如下。bitmap_page结构中的3个字段, map指针本来是要指向page的,但是page没有空间分配,所以就直接将map指针另作它用。指针的大小不论是32位还是64位,其大小至少能容纳下32位,即2个*bmc。那么就直接将map指针的前32位看作是2个*bmc,一个*bmc管理1024个bitmap file缓存中的bit,这样两个*bmc管理2048个bit,正好与正常情况下一个bitmap_page结构管理的bit数目一致,只是管理bit的粒度变大了;而count字段仍然来统计这2048个bit对应有多少脏的chunk的计数。如下图所示。

MD中bit地图源代码分析-数据结构 

 

  3. 实际动态分配的每个内存页,每16bit管理对应bitmap文件的一个bit,一个bit对应一个chunk(数据块)。这16bit在代码中记作*bmc,它的作用如下:

 MD中bit地图源代码分析-数据结构

    NEEDED_MASK——表示该bit对应的chunk是否需要同步;

    RESYNC_MASK——表示该bit对应的chunk是否正在同步;

  NEEDED_MASK和RESYNC_MASK标志是不会同时存在的,盘阵需要同步时,会先设置NEEDED_MASK标志,当下次检查到有NEEDED_MASK标志时,表示需要同步,此时清除该标志,设置RESYNC_MASK标志,进行同步。如果同步出错,则清除RESYNC_MASK标志,设置NEEDED_MASK标志。内存bitmap的另一个作用是在精准恢复或者同步时判断一个bitmap-chunk是否需要恢复或者同步,即NEEDED_MASK和RESYNC_MASK的作用。

  低14位是counter,用来统计对应的chunk尚未完成的写请求的计数。真正的计数值是从2开始累加,表示有写操作(比如有2个未完成写操作,则值为4)。counter的值为0、1、2均为没有写操作,是特殊状态:

    *bmc=0——该bit位需要下刷;

    *bmc=1——该bit位需要清零;

    *bmc=2——该bit对应的chunk上的写操作全部完成,表示该bit可以被清除并下刷。从2开始计数。

  *bmc与bitmap file缓存的对应关系如下如所示:

MD中bit地图源代码分析-数据结构 

  4. Bp数组中的map字段是一个指针,指向一个page页,该page页中顺序存放*bmc,一个page可以存放2048*bmc。一个*bmc对应bitmap file中的一个bit,也就是对应数据中的一个chunk也就是说bp数组的第二项的map指针指向的page页的第一个*bmc,是整体的第2049个*bmc(下标为0称为第1个)。

  *bmc实际作用是控制bitmap的置位与复位,并且也控制一个chunk上的请求不能超过最大值(14bit表示的最大整数),达到最大值的时候会进行IO schedule。

  当内存空间不足够分配*bmc的时候,那么hijacked置位,将map指针本身当做两个*bmc,此时一个*bmc就不再只是对应bitmap file中的一个bit,而是半个page的bit,两个*bmc可以管理正好一个page的bit。也就是说如果空间不够的话,*bmc的管理粒度就增大了。

  数据chunk、bp、*map page和attr都是顺序排列的,可以使用顺序寻找,一一对应的方法将他们关联起来。

 

  5. filemap_attr表示bitmap文件缓存页的属性,使用4bit来表示:

    BITMAP_PAGE_DIRTY——表示内存bitmap有bit被置位,但是bitmap file对应的位没有被置位,因此page需要同步刷到磁盘,写磁盘完成才能继续

    BITMAP_PAGE_CLEAN——这是一个过渡状态,表示内存bitmap有可以清除的bit,则需要清除该bit,然后过渡到BITMAP_PAGE_NEEDWRITE状态;

    BITMAP_PAGE_NEEDWRITE——表示内存bitmap有bit被清除,但是bitmap file对应的位没有被清除,因此page需要刷到磁盘,但是异步进行的,因为即使写失败,最多带来额外的同步,不带来数据的危害

  其在内存中的结构和与bitmap file缓存的关系如下如所示:

MD中bit地图源代码分析-数据结构 

  6. 分析bitmap整体结构关系:

    bitmap在内存中相关结构的关系如下如所示:

MD中bit地图源代码分析-数据结构 

    Bitmap对于内存和磁盘的交互关系如下如所示:

MD中bit地图源代码分析-数据结构 

    为了便于理解,下面给出一个计算实例,以3个page的bit为例。计算过程在红字中标出,如下图所示:

 MD中bit地图源代码分析-数据结构

    将上述结构串联起来,得到一个bitmap的整体结构,如下图所示:

MD中bit地图源代码分析-数据结构 

    上图中,实线代表的是对象关系,虚线代表的是控制关系。

 

  重新回顾一次“概要”一章中的故事。首先当没有bitmap的时候,就只有磁盘中存在有Data而没有其他结构,如图中右下角;当引入bitmap之后,则在磁盘中还存在了一种“数据”叫做bitmap file,bitmap file的一个bit对应盘阵的一个chunk,在盘阵数据写入前,设置该chunk对应的bit,盘阵写入完成,则清除该bit。要进行同步时,参照bitmap,只有置位的bit对应的chunk才需要进行同步,这样缩短了同步的时间,提高了效率。

  bitmap原理很明了,按照这个原理直接进行实施也是可以的,但直接这样实施的话,由于一次数据块的写入多了两次磁盘访问(bitmap的设置和清除),写入效率会受到较大影响,所以还需要考虑一些优化。优化主要是两方面的:

    1、bitmap设置后批量写入;

    2、bitmap延时清除。

  这两方面的优化,需要在内存中构建和磁盘bitmap文件对应的数据bitmap file缓存,bitmap操作首先在缓存中进行,必要时才进行真正的磁盘操作。内存中bitmap file缓存与磁盘上的bitmap file每个bit一一对应,所以内存bitmap file缓存中的一个bit也与对应的磁盘bitmap file中的bit一样对应于Data中的一个chunk。对于bitmap file缓存自身的每一个page都有filemap_attr来管理页状态,并且bitmap file缓存中的每一个bit在内存中都有16个bit的结构*bmc来进行管理。

  bitmap结构体下,**filemap指向bitmap file缓存的一个page,filemap_attr字段管理一个bitmap file缓存page的页状态,bp->map指向一个page(需要时才分配page空间),其中存放的都是*bmc,一个*bmc有16位,每个*bmc用来管理对应的bitmap file缓存的1个bit,bitmap file缓存与磁盘上的bitmap file互相对应,其中每个bit对应了Data中的相应chunk的数据写入状态,就将整个bitmap框架连接了起来。

 

转载出处:http://www.cnblogs.com/fangpei