原创作者: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调度。