从今天开始,我们将介绍一下Linux的存储软件栈,也称为IO栈。今天我们先从整体上了解一下Linux的整个IO栈,后续再深入介绍每个组件。我们很难说清楚Linux的存储软件栈到底有多复杂,但thomas-krenn的一张图可能可以给大家一个比较直观的印象。如下图是作者从thomas-krenn网站上下载的一张图,该图比较详细的描述了Linux的存储栈。
大家看到这张图的第一反应可能是无所适从,或者直接跳过本文。大家不要着急,我们这里会简化介绍一下,期望大家能够对Linux存储软件栈能够有一个比较全面,而又深入的理解。
我们这里将thomas-krenn图简化为下图的样子,简化后的样子就清晰的多了。本文就以这张图为大纲介绍一下Linux存储软件栈的主要功能模块的功能及一些实现细节。本文将从下往上进行介绍,依次为设备驱动、SCSI层、通用块驱动、硬盘冗余、逻辑映射、文件系统和数据库等。
需要说明的是,我们这里是一个简图,所以会缺失一些细节,或者不严谨的地方。比如本图中展示的SCSI协议层,其实并非所有存储设备都经过SCSI协议,可能是SATA或者NVMe协议。再比如LVM和RAID的关系,并非一定图中的关系,也可以反过来。本文只是为了让大家方便理解这些功能的关系。
我们自顶而下的分析一下这个存储软件栈,这个软件栈中除了数据库外都属于操作系统,数据库属于系统软件,而不是操作系统的一部分。之所以将数据库放在这里,是因为数据库是存储数据最常用的系统软件。
数据库有很多种类型,本文以关系型数据库为例介绍。关系型数据库是一种将线性存储空间抽象为表格空间的软件系统。这里所谓的线性存储空间可以是硬盘或者文件系统中的一个文件。表格空间则是指记录数据的格式,如下图所示的电商商品列表信息,可以通过一个二维表格进行存储。而存储数据的二维表在底层则是存储在一个文件中,或者直接存储在硬盘上。
关系型数据库系统核心是实现了线性空间到表空间的转换,但本身是非常复杂的。如下是开源关系型数据库MySQL的架构简图。MySQL分为两个核心层,一个是计算层,负责SQL语句的解析和优化;另外一个是存储层,负责数据的存取,也是实现空间映射的部件。
数据库继续往下则是文件系统层,文件系统实现了线性空间到树型空间的转换。这里的线性空间是指硬盘的存储空间,而树型空间则是指文件系统的目录结构。如下图所示,文件系统可以目录中嵌套目录和文件,形成开枝散叶的效果,类似一棵树。
Linux操作系统支持几十种文件系统,其原理如下图所示。在Linux操作系统中实现了一个虚拟文件系统层(Virtual File System,简称VFS),其它具体文件系统都位于VFS之下。VFS为应用层提供统一的访问接口(API),当请求经过VFS后会被转发到具体的文件系统来处理。关于文件系统IO处理的更多细节,我们在后文《Linux文件系统全析,架构与案例解析》一文中详细介绍,本文不再赘述。
文件系统系统往下是逻辑卷管理层(Logical Volume Manager),也即LVM层。对于文件系统来说,LVM层并非必需,文件系统可以直接构建在裸硬盘或者硬盘分区上。基于LVM构建文件系统的原因在于LVM可以提供弹性的逻辑空间(称为逻辑卷),当出现存储空间不够的情况下,可以实现动态扩容。所以,如果计算节点连接的不是本地硬盘,而是磁盘阵列的情况下,LVM就没有必要了。因为绝大多数的磁盘阵列系统是可以支持LUN的动态扩容的。
相关视频推荐
c/c++存储开发需要掌握哪些知识,自上而下介绍存储架构体系
存储开发,linux内核文件系统的实现
SPDK如何实现高性能,深入NVMe的工作原理
Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
如下图是LVM的逻辑关系图,基于物理硬盘可以构建物理卷(PV),多个物理卷可以构建为一个卷组(VG),在卷组之上可以创建逻辑卷。卷组的概念相当于企业级磁盘阵列中的存储池的概念,这个我们在后文会详细介绍。
物理卷并不一定要基于物理硬盘构建,也可以基于RAID构建。通常,如果基于本地硬盘构建LVM时也是要先构建RAID的,这是因为需要通过RAID来提高数据的可靠性。如果没有RAID,任何一块硬盘的故障就会导致整个数据的丢失。
RAID技术可以理解为一种存储虚拟化技术,他可以将多个物理硬盘抽象为一个虚拟硬盘。其关键点在于虚拟硬盘的可靠性和性能通常都可以得到提升。RAID采用一种校验码的技术,在写入数据的时候会同时计算出一个对应的校验数据,保证在出现硬盘故障的情况下可以重新计算出完整的数据。
如上图所示,可以通过3块100GB容量的硬盘抽象出一个200GB的虚拟硬盘。虽然总统有效容量损失了100GB,但是可以通过损失的空间存储校验数据保证数据的可靠性。RAID的另外一个特性是可以提高性能,因为IO可以被多个硬盘同时处理,这个还是比较容易理解的。
再往下是通用块层,通用块层为上层提供一个线性的存储空间。也就是说,对于一个块设备,无论底层是什么协议(SCSI、NVMe或者是ATA),也不管底层是什么物理设备,但通过通用块层后,用户看到的就是一个线性的地址空间。如下是通用块层的一个架构简图。需要再次强调的是,在Linux内核中各层并非严格遵循我们第一张图中的位置,比如RAID和逻辑卷管理,其实也用到了通用块层的功能。
通用块层往下就涉及到具体的协议了,本文以SCSI协议为例进行介绍。如下图所示,SCSI整个架构分为3层,分别为高层、通用服务层和设备驱动层。中间是通用服务层,用于实现SCSI的公共功能,比如错误处理等。而上面一层称谓高层,它代表各种SCSI设备类型的驱动,如SCSI磁盘驱动,SCSI磁带驱动,高层驱动认领低层驱动发现的SCSI设备,为这些设备分配名称,将对设备的IO转换为SCSI命令,交由低层驱动处理。
最下面的称谓低层,它代表与SCSI的物理接口的实际驱动器,主要为各个厂商为其特定的主机适配器(Host Bus Adapter, HBA)驱动,例如:FC卡驱动、SAS卡驱动和iSCSI(iSCSI可以使硬件HBA卡或者基于普通网卡的软件实现)等。
设备驱动层位于存储软件栈的最底层,该层包含很多不同类型的设备驱动。即使不是计算机背景的通信对设备驱动应该也有所了解。当我们为计算机添加一个硬件的时候,通常需要安装该设备对应的驱动程序。无论是Linux操作系统还是Windows操作系统都是这样。
存储设备其实有很多种类型,最常见的如机械硬盘、SSD硬盘等。除了上述基本的存储设备外,比如RAID卡、FC卡或者是iSCSI卡等都属于存储设备的范畴。对每一种设备的操作都是需要相应的软件的,这类软件称为驱动程序。
我们深入到Linux的内核代码,驱动程序相关的源代码都在drivers目录下面。比如块设备驱动、蓝牙驱动、网卡驱动和存储领域常见的光纤卡驱动等都在这里。以蓝牙驱动为例,这里有一个名称为bluetooth目录,所有蓝牙相关的驱动都在这里。因为各个厂商的蓝牙设备存在差异,所以每个厂商的蓝牙设备通常都有一个驱动程序。
至此,我们简要介绍了一下Linux存储软件栈的主要部件及基本的功能。