原创:DRBD实现原理分析

原创作者:Li Changlong   Email: 

转载请标明出处

 

DRDB(Distributed Replicated Block Device)由内核模块和相关应用层工具程序组成,主要被用于Linux平台下的高可用(HA)方案之中,实现不同机器上数据的同步,保持数据的一致性。当本地节点的主机出现故障时,远程节点的主机上还会保留有一份完全相同的数据,可以继续使用,以达到高可用的目的。在高可用(HA)解决方案中使用DRBD,可以代替使用一个共享盘阵列存储设备。因为数据同时存在于本地主机和远程主机上,在遇到需要切换的时候,远程主机只需要使用它上面的那份备份数据,就可以继续提供服务了。


  

图一

如图一所示,DRBD位于文件系统的下层,他将磁盘数据的变化同步到对端机器,并不关心上层数据的类型,也不检测上层的错误,例如上层的文件系统崩溃了,DRBD是检测不到的,他同样将数据同步到对端。

       DRBD的使用手册非常完备,网上的功能介绍也很多,本文试图从开发者的角度,从代码实现的层面分析DRBD的原理及依赖的相关Linux内核技术。

       DRBD是一个虚拟的块设备,实现了一个块设备驱动程序,主设备号是147,每一个DRBD块设备对应一个实际的物理设备,Mount磁盘分区时,使用的是DRBD设备,例如/dev/drbd0等。

与块设备相关的内核组件是非常多的,参见图2,要想理解DRBD的工作原理,首先需要了解Linux文件系统中一个普通的文件I/O操作的执行过程,也就是要对图2所示的结构有一个整体的了解,这里不做深入介绍,可以阅读《Understanding The Linux Kernel》的第12、14、16章节。

通用块层:

Generic Block Layer,通用块层,它处理来自系统中所有块设备发出的请求。通用块层的核心数据结构是一个称为bio的描述符,他描述了块设备的I/O操作。每个bio结构包括一个设备描述符,I/O操作的起始磁盘扇区号和扇区数目,内存段等,几个关键字段如下所示:

struct bio {

       sector_t          bi_sector; //块IO操作的第一个磁盘扇区

       struct block_device       *bi_bdev;//指向块设备描述符的指针,此次IO操作的磁盘设备,例如

                                                 // /dev/sda1等

       unsigned long         bi_rw;     /* IO操作的类型,读或者写等*/

       struct bio_vec        *bi_io_vec;     /*执行内存段的指针*/

    …………….. //其他字段没有列出

};


图二

当通用块层启动一次新的IO操作时,会调用alloc_bio()函数分配一个新的bio结构,执行相应的赋值操作后,内核就调用generic_make_request()函数,它是通用块层的主要入口点,generic_make_request(bio)做完一些工作后会调用块设备驱动程序的make_request_fn方法将bio请求插入块设备的请求队列。

请求队列:

磁盘是一个由通用块层处理的逻辑块设备,通过块设备驱动程序加载后,使用block_device结构表示,每一个磁盘驱动程序都会维护一个磁盘请求队列request_queue,一些字段如下所示:

struct request_queue

{

       struct list_head       queue_head;

       struct request         *last_merge;

       elevator_t              *elevator;

       request_fn_proc            *request_fn;

       merge_request_fn   *back_merge_fn;

       merge_request_fn   *front_merge_fn;

       merge_requests_fn *merge_requests_fn;

       make_request_fn           *make_request_fn;//把一个新请求插入请求队列时调用的方法

       …………..  //其他字段没有列出

};

我们现在只关心make_request_fn函数,它由每一个设备驱动程序提供,通用块层调用此函数将新的IO请求放入具体磁盘设备的IO请求队列。

 

一个具体IO操作的过程:

首先是应用程序调用read()或write()系统调用

此调用转化为每一个具体文件系统实现的读写操作,每个文件系统实现的读写操作最终会调用通用块层的接口,生成IO请求,放入具体磁盘的IO请求队列,以EXT3文件系统为例:

      调用bio_alloc()分配一个新的bio结构,将bio赋值后调用generic_make_request(bio)函数,磁盘函数会调用磁盘驱动程序定义的make_request_fn函数,将IO请求放入磁盘请求队列。

DRBD的实现:

DRBD实现了一个虚拟的块设备,DRBD设备的驱动程序中定义了make_request_fn函数,此函数捕获了所有的上层的IO操作,将修改的数据同步到对端,同时将bio的bi_bdev字段修改为具体的物理磁盘设备标示符,然后再调用generic_make_request(bio)函数,将IO请求插入磁盘设备的请求队列。

使用DRBD的设备的具体IO操作过程:

      文件系统调用bio_alloc()分配一个新的bio结构,bio结构的bi_bdev字段为drbd设备的标示符,然后调用generic_make_request(bio)函数,此函数调用drbd驱动程序定义的make_request_fn函数,make_request_fn函数将修改的数据同步到对端,同时将bio的bi_bdev字段修改为具体的物理磁盘设备标示符,然后再调用generic_make_request(bio)函数,此时会调用物理磁盘设备的make_request_fn函数,将IO请求插入磁盘设备的请求队列,开始磁盘IO调度。

你可能感兴趣的:(linux学习,杂项)