Linux操作系统的储存子系统应当是Linux中最为复杂的子系统了。虽然好多子系统都觉得自己是最复杂的子系统,例如显存子系统和网路子系统也如此说。无论怎样,储存子系统在Linux中是比较复杂的。明天我们就介绍一下Linux的储存子系统中的硬碟与RAID的相关内容,前面再写一篇关于LVM与文件系统的内容。
硬碟
在Linux的储存子系统中,最底层的就是硬碟了。这儿的硬碟并不是指我们看见的硬碟硬件,而是指在Linux内部听到的硬碟设备,或则说是块设备。假如我们在/dev目录执行以下ls命令,就可以看见好多设备。在这种设备中以sd开头的就是基于SCSI合同的硬碟。
图1Linux中的块设备
无论是基于SAS、iSCSI还是FC的c盘设备,大约都是这个样子。酷似dm-X的是DeviceMap块设备,也就是通过LVM进行管理的设备,这些设备是一种逻辑设备。
在Linux操作系统中块设备的种类好多,有本地c盘设备、有SAN设备还有基于网路的块设备。在虚拟机中块设备又呈现为另外一种文件名,例如在Xen虚拟机中为xvdX。
尽管名称差别很大,而且在Linux操作系统内核中的实现却十分简单。在内核中任何c盘块设备都是通过调用add_disk函数完成的。在《Linux设备驱动程序》这本书对块设备进行了详尽的介绍,而且可以通过特别简单的代码实现一个自己的块设备。
图2最简单的块设备驱动
这儿面有2个函数,也就是alloc_disk和add_disk。前一个函数是分配一个通用块的结构体,前者则是将该块设备添加到内核,也就是在/dev目录下生成一个“文件”。以上述代码为例,执行后会生成如下块设备。
brw-rw----1rootdisk251,0Jun1609:13/dev/sbulla
这儿我们自定义了一个设备名称sbulla。虽然我们看见的SCSI设备也是这样定义的linux 字符设备驱动,只不过其定义名称的时侯是通过sd字符。
以上述代码为例,在块设备中比较重要的地方是初始化了一个队列处理函数(sbull_full_request)。所有从下层访问该块设备的恳求就会转发到该处理函数进行处理。
所有块设备都要初始化这个队列,但是提供一个恳求处理函数。不同的块设备的恳求处理函数略有不同。例如常见的SCSI块设备,其处理函数初始化过程如下:
q=__scsi_alloc_queue(sdev->host,scsi_request_fn);
而nbd(网路块设备,通过网路的形式将服务端的文件映射为顾客端的块设备)设备的初始化队列的代码如下所示:
disk->queue=blk_init_queue(do_nbd_request,&nbd_lock);
类似的反例还好多,本文不再一一介绍。这儿我们须要理解一点,核心问题在于注册处理恳求的反弹函数,以及通过add_disk就可以在/dev目录下边创建一个块设备。
另外一点,对于任何类型的块设备,无论是本地硬碟,还是经过网路的NBD和iSCSI,还是FC设备,最后都是/dev目录下的一个文件,而这个文件或许就是块设备。我们可以通过对该文件的读写实现对块设备的访问。
RAID
作为普通用户使用单个硬碟是没有任何问题的,并且作为企业应用使用单个硬碟存在很大的风险。这时由于硬碟随时有可能受损,因而我们须要一种机制来保证虽然出现硬碟故障的情况下,数据不会遗失,且业务一直可以正常工作。
RAID正是解决上述问题的技术。RAID的全称为廉价冗余c盘阵列(RedundantArrayofInexpensiveDisks),从字面可以看出其基本原理就是通过廉价的c盘组成一组c盘。RAID不仅仅可以通过冗余的方法解决数据可靠性的问题,还可以增强性能。其主要原理就是将恳求分拆到多个化学硬碟来执行,性能自然比一个硬碟快了。
在Linux操作系统层面,虽然就是将化学c盘通过软件具象为逻辑c盘。以RAID1(两块c盘储存相同的数据linux重启命令,在出现一块c盘故障的情况下,数据不遗失)为例,通过Linux内核中的软件创建一个虚拟的块设备,而该块设备中记录了底层对应的数学设备及相关参数。
图3RAID1示意图
因而,从用户层面来看就是一块普通的c盘设备,而在底层却是2个独立的化学硬碟。当用户向逻辑c盘写数据的时侯,其中的软件会通过参数进行估算,并将数据重新定向到底层的化学设备。通过这些方式可以保证虽然出现某个数学c盘受损,用户的数据依然完好无损。
不仅里面说的RAID1外,还有好多RAID类型。不同的RAID类型实现不同的功能。例如RAID0实现条带化,主要是提高性能;RAID1则是实现数据的冗余,避免c盘故障造成的数据遗失;因为上述RAID只能解决一方面的问题,因而有人讲三者结合,出现了RAID10和RAID01,这样既能保证数据的可靠性,又能提高性能。
因为RAID1是一份数据讲到两个设备,因而只有50%的有效数据。为了提升有效数据率,于是发明了RAID5和RAID6等类型。其中RAID5通过降低一个校准数据来保证数据的可靠性,以5块盘的RAID5为例,其中有效数占4块盘的空间,有效数据80%。并且RAID5有个问题,就是一组c盘中只能坏一块,假如受损的c盘超过1块都会造成数据遗失。RAID6的算法与RAID5类似linux 字符设备驱动,它的特征是可以容忍2块c盘故障。
在实现层面kali linux,Linux的RAID实现在用户态和内核态都有涉及。其中用户态主要进行RAID的管理,而内核态一方面配合用户态进行RAID管理,另外一方面则实现对IO的处理,这部份才是RAID最为核心的内容。
图4软件构架
对于基于SCSI化学c盘的RAID来说,Linux环境下整个软件构架如图4所示。其中实线以上的为用户态的软件模块,实线以下的为内核态的软件模块。这儿比较核心的是RAID公共层,在这儿主要创建md设备,该设备是一个逻辑设备,也是用户可以看见的RAID设备。其下则是具体的RAID模块,用于实现不同的RAID级别(算法)。
再往下就是通用SCSI驱动层了,也就是图中的SCSIc盘驱动这一层的内容。该层虽然是SCSI系统的下层驱动(SCSI子系统分为上中下三层)。RAID模块通过调用该层的数据访问插口就可以实现化学c盘数据读写了。