当前位置: 首页 > 科技观察

硬盘与RAID

时间:2023-03-20 23:35:08 科技观察

Linux操作系统存储子系统核心技术Linux操作系统的存储子系统应该是Linux中最复杂的子系统了。事实上,许多子系统都认为自己是最复杂的子系统,例如内存子系统和网络子系统。无论如何,Linux中的存储子系统更加复杂。今天先介绍一下Linux存储子系统中的硬盘和RAID,稍后再写一篇关于LVM和文件系统的文章。硬盘是Linux存储子系统最底层的硬盘。这里的硬盘不是指我们看到的硬盘硬件,而是指Linux内部看到的硬盘设备,或者说块设备。如果我们在/dev目录下执行如下ls命令,我们可以看到很多设备。在这些设备中,以sd开头的是基于SCSI协议的硬盘。图1Linux中的块设备,无论是基于SAS、iSCSI还是FC的磁盘设备,大概是这样的。与dm-X类似的是DeviceMap块设备,它是由LVM管理的设备。该设备是逻辑设备。Linux操作系统中有多种类型的块设备,包括本地磁盘设备、SAN设备和基于网络的块设备。在虚拟机中,块设备以另一个文件名的形式呈现,如Xen虚拟机中的xvdX。虽然名称差异很大,但在Linux操作系统内核中的实现非常简单。内核中任何磁盘块设备都是通过调用add_disk函数来完成的。《Linux设备驱动程序》一书中对块设备进行了详细的介绍,你可以用非常简单的代码实现自己的块设备。图2最简单的块设备驱动有2个函数,分别是alloc_disk和add_disk。前者的作用是分配一个普通的块结构,后者是将块设备添加到内核中,即在/dev目录下生成一个“文件”。以上面代码为例,执行后会生成如下块设备。brw-rw----1rootdisk251,0Jun1609:13/dev/sbulla这里我们自定义了一个设备名sbulla。其实我们看到的SCSI设备也是这样定义的,只不过用sd字符来定义名称。以上面的代码为例,块设备中比较重要的部分是初始化一个队列处理函数(sbull_full_request)。所有来自上层的访问块设备的请求都会被转发给这个处理函数进行处理。所有块设备都必须初始化这个队列并提供一个请求处理程序。不同的块设备具有略微不同的请求处理程序。例如对于普通的SCSI块设备,处理函数初始化过程如下:q=__scsi_alloc_queue(sdev->host,scsi_request_fn);而nbd(网络块设备,通过网络将服务器的文件映射到客户端的块设备)device初始化队列的代码如下:disk->queue=blk_init_queue(do_nbd_request,&nbd_lock);类似的例子还有很多,本文不再一一介绍。这里我们需要明白核心问题是注册处理请求的回调函数,通过add_disk在/dev目录下创建块设备。另外,对于任何类型的块设备,不管是本地硬盘,还是通过网络的NBD和iSCSI,或者FC设备,最后都是/dev目录下的一个文件,而这个文件其实就是一个块设备。我们可以通过读写文件来访问块设备。作为普通用户使用单硬盘RAID没有问题,但是作为企业应用使用单硬盘就存在很大的风险。这个时候,因为硬盘随时可能损坏,所以我们需要一种机制,保证即使硬盘出现故障,数据也不会丢失,业务依然可以正常进行。RAID就是解决上述问题的技术。RAID的全称是廉价磁盘冗余阵列(RedundantArrayofInexpensiveDisks)。从字面上可以看出,它的基本原理是通过便宜的磁盘组成一组磁盘。RAID不仅可以通过冗余解决数据可靠性问题,还可以提高性能。主要原理是将请求拆分到多个物理硬盘上执行,性能自然比单个硬盘快。在Linux操作系统层面,实际上是通过软件将物理磁盘抽象为逻辑磁盘。以RAID1(两块磁盘存储相同数据,万一出现磁盘故障,数据不会丢失)为例,在Linux内核中通过软件创建一个虚拟块设备,并记录下底层对应的数据在块设备中。物理设备及相关参数。图3RAID1示意图因此,从用户的角度来看,它是一个普通的磁盘设备,但在底层有两个独立的物理硬盘。当用户向逻辑磁盘写入数据时,其中的软件会计算参数并将数据重定向到底层物理设备。这种方法可以保证即使物理磁盘损坏,用户的数据仍然完好无损。除了上面提到的RAID1,还有很多RAID类型。不同的RAID类型实现不同的功能。比如RAID0实现条带化,主要是为了提高性能;RAID1实现数据冗余,防止磁盘故障导致数据丢失;因为上面提到的RAID只能解决一方面的问题,所以有人说两者结合形成RAID10。和RAID01,既能保证数据的可靠性,又能提高性能。由于RAID1中一份数据写入两个设备,因此只有50%的数据是有效的。为了提高有效数据速率,发明了RAID5和RAID6等类型。其中,RAID5通过增加一个验证数据来保证数据的可靠性。以5块RAID5为例,有效数据占用4块磁盘空间,有效数据占80%。但是RAID5有一个问题,就是一组中只能有一个磁盘损坏,如果有多个磁盘损坏,数据就会丢失。RAID6的算法与RAID5类似,其特点是可以容忍两个磁盘故障。在实现层面,Linux的RAID实现涉及用户态和内核态。其中用户态主要管理RAID,而内核态配合用户态一方面管理RAID,另一方面实现IO的处理。这部分是RAID的核心内容。图4软件架构基于SCSI物理磁盘的RAID,Linux环境下的整个软件架构如图4所示,虚线以上的软件模块为用户态软件模块,虚线以下为内核态软件模块模式软件模块。这里的核心是RAID公共层,这里主要创建md设备。这个设备是逻辑设备,也是用户可以看到的RAID设备。下面是具体的RAID模块,用于实现不同的RAID级别(算法)。再往下就是通用SCSI驱动层,也就是图中SCSI磁盘驱动层的内容。这一层实际上是SCSI系统的上层驱动(SCSI子系统分为上、中、下三层)。RAID模块通过调用本层的数据访问接口实现对物理磁盘数据的读写。