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

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

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

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

  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缓存的关系如下图所示。

  2. 其中struct bitmap_page结构如下:

    struct bitmap_page {

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

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

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

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

                   */

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

    };

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

  在正常情况下(内存充足),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计数。如下图所示。

  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的计数。如下图所示。

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

    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缓存的对应关系如下如所示:

  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缓存的关系如下如所示:

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

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

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

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

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

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

  重新回顾一次“概要”一章中的故事。首先当没有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

MD中bitmap源代码分析--数据结构的更多相关文章

  1. MD中bitmap源代码分析--入题概述

    在MD模块中,各级raid都使用的一份bitmap的源码,也就是说共用一种bitmap的流程,下面以raid1的使用为例来分析bitmap的工作原理. 在使用raid1磁盘阵列的时候,对于数据的可靠性 ...

  2. MD中bitmap源代码分析--SYNC IO和RAID5的补充

    最近在做bwraid的R6的设计工作,需要调研一下bitmap下刷磁盘的IO属性(是否为SYNC IO),还有raid5中bitmap的存储和工作方式. 1.bitmap刷磁盘是否为 SYNC IO? ...

  3. MD中bitmap源代码分析--清除流程

    bitmap的清零是由bitmap_daemon_work()来实现的.Raid1守护进程定期执行时调用md_check_recovery,然后md_check_recovery会调用bitmap_d ...

  4. MD中bitmap源代码分析--状态机实例

    1. page_attrs的状态转换关系 之前说过,bitmap的优化核心是:bitmap设置后批量写入:bitmap延时清除.写bit用bitmap_statrwrite() + bitmap_un ...

  5. MD中bitmap源代码分析--设置流程

    1. 同步/异步刷磁盘 Bitmap文件写磁盘分同步和异步两种: 1) 同步置位:当盘阵有写请求时,对应的bitmap文件相应bit被置位,bitmap内存页被设置了DIRTY标志.而在下发写请求给磁 ...

  6. Memcached源代码分析 - Memcached源代码分析之消息回应(3)

    文章列表: <Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)> <Memcached源代码分析 - Memcached源代码分析 ...

  7. Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6633311 在上一篇文章中,我 们分析了And ...

  8. Java中arraylist和linkedlist源代码分析与性能比較

    Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arra ...

  9. FreeBSD 5.0中强制访问控制机制的使用与源代码分析【转】

    本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容.这一部分讲述要将MAC框架与 ...

随机推荐

  1. RDIFramework.NET平台代码生成器V1.0发布(提供下载)

    RDIFramework.NET平台代码生成器V1.0发布(提供下载)   RDIFramework.NET(.NET快速开发整合框架)框架做为信息化系统快速开发.整合的框架,其目的一至是给用户和开发 ...

  2. Android四大组件——Service

    Service相关链接 Service初涉 Service进阶 Service精通 Service是Android系统中的一种组件,它跟Activity的级别差不多,但是它不能自己运行,只能后台运行, ...

  3. automatically select architectures

    各位在用XCode 5.x 打开用XCode 4.x 创建的项目时候.会遇到编译器警告automatically select architectures. 1. This is because th ...

  4. LSI SAS 2208 配置操作

    配置LSISAS2208 介绍LSISAS2208扣卡的配置方法. 2.1 登录CU界面 介绍登录LSISAS2208的CU配置界面的方法,以及CU界面的主要功能. 2.2 创建RAID 介绍创建RA ...

  5. REMOTE HOST IDENTIFICATION HAS CHANGED

    今天在使用scp命令上传文件到远端服务器时报如下错误,(ssh命令连接到远程服务器时也报同样的错误): @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  6. hdu_1875_畅通工程再续 prim和kruskal

    这个题是个典型的最小生成树的题目,但是刚开始怎么都过不了,后来发现两种写法都有疏忽,但是prim的到目前为止不懂为什么刚开始的不对,kruskal算法,并查集的初始条件从0开始写成从1开始了,所以已知 ...

  7. NYOJ 214 最长上升子序列nlogn

    普通的思路是O(n2)的复杂度,这个题的数据量太大,超时,这时候就得用nlogn的复杂度的算法来做,这个算法的主要思想是只保存有效的序列,即最大递增子序列,然后最后得到数组的长度就是最大子序列.比如序 ...

  8. IO-文件 File 复制 读写 总结

    一定要注意: 传入的参数,应该是包含文件名的完整路径名,不能把一个文件复制到[文件夹]中,因为[文件夹]本身是不能有输入输出流的,只能复制到一个[文件]中,否则会报异常. 以字节流读写的三种方式 pu ...

  9. Android-Eclipse汉化

    首先下载好汉化包 解压后得到eclipse文件夹 然后在eclipse根目录下创建language文件夹 将eclipse文件夹放入 在eclipse根目录下创建links文件夹 然后在里面添加这个文 ...

  10. nyoj 76

    #include <iostream> using namespace std; int main() { int i,t,n; int a[101]; cin>>t; whi ...