文章目录
- 一、磁盘简介
- 1.1 简介
- 1.2 机械硬盘与固态硬盘
- 1.2.1 机械磁盘(HDD)
- 1.2.2 固态磁盘(SSD)
- 1.2.3 I/O操作
- 二、文件系统简介
- 2.1. 简介
- 2.2 文件系统特点
- 2.3 Linux文件系统
- 三、文件数据存储方式
- 3.1 连续存储
- 3.2 链接表存储
- 3.3 inode存储
- 四、通用块层
- 五、I/O栈
- 参考资料
一、磁盘简介
1.1 简介
磁盘是一种计算机存储设备,用于持久性地存储和读取数据。它通常由一个或多个盘片(也称为磁盘碟片)构成,每个盘片都有一个或多个磁道和扇区。磁盘通过旋转盘片并使用磁头读写数据来实现数据存储和访问。
磁盘设备都有以下性质:
• 是一组线性排列的磁盘块;
• 可以访问其中的任意磁盘块;
• 可以独立地读/写磁盘块;
• 如果在磁盘块中写入数据,将被记录下来,并在以后的读访问中返回这些数据。
以下是磁盘的一些关键概念:
(1)物理结构:磁盘由一个或多个盘片组成,每个盘片都有一个或多个磁道和扇区。磁道是圆形的磁性区域,扇区是磁道上的一个小区域,用于存储数据。磁头是用于读取和写入数据的机械装置,它位于磁盘上方或下方,并在盘片旋转时移动到指定的磁道上。
(2)逻辑结构:磁盘的逻辑结构是通过将磁道和扇区组织成一个线性地址空间来实现的。每个扇区都有一个唯一的地址,可以通过地址来访问特定的数据。逻辑块是文件系统使用的最小数据单元,通常对应于一个或多个扇区的大小。
扇区一般大小为512字节。逻辑块大小必须是扇区大小的2的整数倍,并且要小于页面的大小。逻辑块的大小是512字节、1KB、或者4KB。常见的逻辑块的大小是4KB,也就是说,连续8个扇区,或者单独的一个页,都可以组成一个逻辑块。
因此常见情况下:
扇区 = 512字节,逻辑块 = 8 * 512 = 4KB
Linux 内核的最小寻址单元是逻辑块不是扇区。
(3)磁盘容量:磁盘容量指的是磁盘可以存储的数据量。它通常以字节为单位进行表示,常见的单位有千字节(KB)、兆字节(MB)、千兆字节(GB)、特兆字节(TB)等。磁盘容量取决于磁盘的物理特性和制造技术。
(4)磁盘访问时间:磁盘访问时间是指从发出读写请求到数据可供使用的时间。它通常由寻道时间、旋转延迟时间和数据传输时间组成。寻道时间是磁头移动到指定磁道所需的时间,旋转延迟时间是等待所需数据旋转到磁头位置的时间,数据传输时间是将数据从磁盘传输到计算机内存或反之所需的时间。
(5)磁盘接口:磁盘通过特定的接口与计算机系统进行通信。常见的磁盘接口包括IDE(Integrated Drive Electronics)、SATA(Serial ATA)、SAS(Serial Attached SCSI)和SCSI(Small Computer System Interface)等。接口类型决定了磁盘与计算机系统的连接方式和数据传输速率。不同的接口,往往分配不同的设备名称。比如, IDE 设备会分配一个 hd 前缀的设备名,SCSI和SATA设备会分配一个 sd 前缀的设备名。如果是多块同类型的磁盘,就会按照a、b、c等的字母顺序来编号。
(6)磁盘阵列:磁盘阵列(RAID)是将多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列,以提供更高的性能、容错能力和容量的技术。磁盘阵列可以通过数据分割、冗余校验和数据条带化等方法来实现数据的分布和冗余存储,从而提高数据访问的速度和可靠性。根据容量、性能和可靠性需求的不同,RAID一般可以划分为多个级别,如RAID0、RAID1、RAID5、RAID10等。
RAID0有最优的读写性能,但不提供数据冗余的功能。
而其他级别的RAID,在提供数据冗余的基础上,对读写性能也有一定程度的优化。
其实在 Linux 中,磁盘实际上是作为一个块设备来管理的,也就是以块为单位读写数据,并且支持随机读写。每个块设备都会被赋予两个设备号,分别是主、次设备号。主设备号用在驱动程序中,用来区分设备类型;而次设备号则是用来给多个同类设备编号。
块设备的最小寻址单元是扇区,块设备无法比对扇区还小的单位进行寻址操作。而对于Linux内核来说,其最小的逻辑寻址单位是块,块是文件系统的一种抽象,只能基于块来访问文件系统。虽然物理磁盘寻址是按照扇区进行的,但是Linux内核执行的所有磁盘操作都是按照块来进行的。
因此扇区是设备的最小寻址单元,逻辑块是文件系统的最小寻址单元,即逻辑块是Linux内核的最小寻址单元。
1.2 机械硬盘与固态硬盘
机械磁盘(Hard Disk Drive,HDD)和固态磁盘(Solid State Drive,SSD)是两种常见的存储设备。
1.2.1 机械磁盘(HDD)
工作原理:机械磁盘使用旋转的盘片、移动的磁头和磁性表面来存储和读取数据。数据通过磁头在盘片上的磁道上进行读写操作,磁头需要寻找和定位数据所在的磁道,然后等待数据旋转到磁头位置进行读取或写入。
特点:机械磁盘具有较大的存储容量和相对较低的成本。它们适用于存储大量数据和对成本较为敏感的应用场景。然而,由于机械部件的存在,机械磁盘的读写速度较慢,且容易受到物理冲击和震动的影响。
显然,如果 I/O 请求刚好连续,那就不需要磁道寻址,自然可以获得最佳性能。这其实就是我们熟悉的,连续 I/O的工作原理。与之相对应的,当然就是随机 I/O,它需要不停地移动磁头,来定位数据位置,所以读写速度就会比较慢。
机械磁盘的最小读写单位是扇区,一般大小为512字节。每一层里分多个磁道,每个磁道分多个扇区,每个扇区是 512 个字节。
文件系统会把连续的扇区或页,组成逻辑块,然后以逻辑块作为最小单元来管理数据。常见的逻辑块的大小是4KB,也就是说,连续8个扇区,或者单独的一个页,都可以组成一个逻辑块。
1.2.2 固态磁盘(SSD)
工作原理:固态磁盘使用闪存存储芯片而不是机械部件来存储数据。数据通过电子信号在芯片上的存储单元进行读写操作,读取速度更快且无需寻道和旋转等机械操作。
特点:固态磁盘具有更快的数据访问速度、更低的延迟和较小的体积。它们适用于需要高性能和快速响应的应用,如操作系统启动、应用程序加载和数据传输等。此外,固态磁盘也具有较低的功耗和较高的抗震动能力,因为它们没有机械部件。
注意:固态磁盘的寿命受到写入次数的限制,尽管现代固态磁盘具有增强的耐用性和寿命管理技术,但长期高强度的写入操作可能会对其寿命产生影响。
固态磁盘不需要磁道寻址,所以,不管是连续I/O,还是随机I/O的性能,都比机械磁盘要好得多。
固态磁盘的最小读写单位是页,通常大小是4KB、8KB等。
1.2.3 I/O操作
论机械磁盘,还是固态磁盘,相同磁盘的随机 I/O 都要比连续 I/O 慢很多,原因:
(1)对机械磁盘来说,由于随机I/O需要更多的磁头寻道和盘片旋转,它的性能自然要比连续I/O慢。
(2)对固态磁盘来说,虽然它的随机性能比机械硬盘好很多,但同样存在“先擦除再写入”的限制。随机读写会导致大量的垃圾回收,所以相对应的,随机I/O的性能比起连续I/O来,也还是差了很多。
(3)连续I/O还可以通过预读的方式,来减少I/O请求的次数,这也是其性能优异的一个原因。很多性能优化的方案,也都会从这个角度出发,来优化I/O性能。
二、文件系统简介
2.1. 简介
文件系统是计算机系统中用于组织和存储文件和目录的一种管理机制。它定义了文件和目录的命名规则、结构、存储方式以及对它们进行访问和管理的方法。
文件系统的主要目标是提供一种有序的方式来管理计算机存储设备(比如磁盘)上的数据,并使用户能够方便地访问和操作文件。它负责将数据组织成文件和目录的层次结构,并为文件分配存储空间、跟踪文件的位置和属性信息。
以下是文件系统的一些关键概念:
(1)文件:文件是存储在计算机存储设备上的数据单元。它可以是文本文件、图像文件、音频文件、程序文件等。文件系统通过文件名来标识和引用文件。
(2)目录:目录是用于组织文件的容器。它可以包含文件和其他目录,形成一个层次结构。目录提供了一种逻辑方式来组织和浏览文件。
(3)文件路径:文件路径是唯一标识文件在文件系统中位置的字符串。它描述了从根目录到文件的层次结构路径。
(4)文件属性:文件属性描述了文件的元数据,如文件大小、创建时间、修改时间、访问权限等。
(5)文件操作:文件系统提供了对文件的基本操作,如创建、打开、读取、写入、删除和重命名等。
(6)文件系统格式化:文件系统格式化是在存储设备上创建文件系统的过程。它为存储设备分配文件系统所需的数据结构和元数据,使其能够存储和管理文件。
(7)挂载:挂载是将文件系统连接到计算机的过程,使其可以在文件系统层次结构中访问和操作文件。挂载通常涉及将存储设备与文件系统的特定位置(挂载点)关联起来。
不同的操作系统和计算机平台支持不同的文件系统类型,每种文件系统都有其特定的特性、性能和用途。常见的文件系统包括FAT、NTFS(Windows)、ext系列(Linux)、APFS(MacOS)等。选择适合的文件系统类型取决于特定的需求和使用场景。
2.2 文件系统特点
(1)文件系统层次结构:文件系统通常采用层次结构来组织文件和目录。根目录位于层次结构的顶层,它包含其他文件和目录。子目录可以包含更多的文件和目录,从而形成树状结构。这种层次结构使得文件系统具有逻辑和有序的组织。
文件使用一种层次的方式来管理。层次中的节点被称为目录(Directory),而叶子就是文件。目录包含了一组文件和/或其他目录。包含在另一个目录下的目录被称为子目录,而前者被称为父目录,这样,就形成了一个层次的、或者称为树状结构。层次的第一个、或者称为最顶部的目录,称为根(Root)目录。它有点类似树的根——所有的分支都是从这个点开始。根目录或者没有父目录,或者说其父目录为自身。
(2)文件系统的存储方式:文件系统可以使用多种方式来存储文件数据。传统的文件系统将文件存储在物理存储介质(如硬盘)的扇区或块上。现代文件系统还支持更高级的存储方式,如日志文件系统、写时复制和快照等。
(3)文件系统的访问控制:文件系统通过访问权限来控制对文件和目录的访问。访问权限通常包括读取、写入和执行等权限。文件系统还可以支持访问控制列表(ACL)和用户组权限,以提供更精细的权限管理。
(4)磁盘配额:文件系统可以实现磁盘配额的功能,允许管理员对用户或用户组的存储空间进行限制。磁盘配额可以帮助控制存储资源的使用,防止滥用和资源耗尽。
(5)日志和一致性:许多文件系统具有日志功能,用于记录文件系统操作和更改。日志可以提高文件系统的可靠性和恢复性,以及减少数据损坏的风险。此外,一致性机制确保在系统故障或意外断电时,文件系统可以恢复到一致的状态。
(6)文件系统的跨平台兼容性:不同的操作系统支持不同的文件系统类型。为了实现跨平台兼容性,一些文件系统采用通用标准,如FAT32、exFAT等。同时,一些操作系统也提供对其他操作系统文件系统的支持,例如Linux对于Windows的NTFS、FAT等的支持,以及Windows对于Linux的ext系列文件系统的支持。
(7)文件系统的性能和优化:文件系统的设计和实现对于性能具有重要影响。一些文件系统采用了缓存、预读取、延迟写入等技术来提高性能。还有一些文件系统针对特定应用场景进行了优化,例如专门用于大规模数据存储的分布式文件系统。
文件系统是计算机系统中数据管理的核心部分。它们提供了一种结构化和有序的方式来组织和存储文件,使用户能够方便地访问和管理数据。
2.3 Linux文件系统
Linux支持各种不同文件系统。此外,为了保证Linux的开放性,还必须方便用户开发新的文件系统。为了实现这一目的,就必须从种类繁多的各种具体文件系统中提取出它们的共同部分,设计出一个抽象层,让上层应用程序可以通过统一的界面进行操作,当需要具体文件系统介入时,由抽象层调用具体文件系统的回调函数来处理。
Linux文件系统被分为两层:
(1)虚拟文件系统
(2)具体的文件系统
上层为虚拟文件系统开关(Virtual Filesystem Switch)层,简称为虚拟文件系统(VFS)。它是具体文件系统和上层应用之间的接口层,将各种不同文件系统的操作和管理纳入一个统一的框架,使得用户不需要关心各种不同文件系统的实现细节。严格说来,VFS并不是一种实际的文件系统。它只存在于内存中,不存在于任何外存空间。VFS在系统启动时建立,在系统关闭时消亡。VFS由超级块、inode、dentry、vfsmount等信息组成。
下层是具体的文件系统实现,如Minix、EXT2/3/4、sysfs等。具体文件系统实现代码组织成模块形式,向Linux VFS注册回调函数,处理和具体文件系统密切相关的细节操作。
Linux基于公共文件模型(Common File Model)构造虚拟文件系统。这里所谓的公共文件模型,有两个层次的含义:对于上层应用程序,它意味着统一的系统调用,以及可预期的处理逻辑;而相对于具体文件系统,则是各种具体对象的公共属性以及操作接口的提取。
在公共文件模型中,文件是文件系统最基本的单位,如同磁盘块之相对于磁盘设备。每个文件都有文件名,以方便用户引用其数据。此外,文件还具有一些其他信息,例如,文件创建日期、文件长度等。我们把这些信息称为文件属性(File Attribute)。
文件使用一种层次的方式来管理。层次中的节点被称为目录(Directory),而叶子就是文件。目录包含了一组文件和/或其他目录。包含在另一个目录下的目录被称为子目录,而前者被称为父目录,这样,就形成了一个层次的、或者称为树状结构。层次的第一个、或者称为最顶部的目录,称为根(Root)目录。它有点类似树的根——所有的分支都是从这个点开始。根目录或者没有父目录,或者说其父目录为自身。
每个文件系统并不是独立使用的。相反,系统有一个公共根目录和全局文件系统树,要访问一个文件系统中的文件,必须先将这个文件系统放在全局文件系统树的某个目录下。这个过程被称为文件系统装载(Mount),所装载到的目录被称为装载点(Mount Point)。
文件通过路径(Path)来标识。路径指的是从文件系统树中的一个节点开始,到达另一个节点的通路。路径通常表示成中间所经过的节点(目录或文件)的名字,加上分隔符,连接成字符串的形式。如果从根目录开始,则称为绝对路径。如果从某特定目录开始,则称为相对路径。
在目录下,还可以有符号链接。符号链接(Symlink)实际上是独立于它所链接目标存在的一种特殊文件,它包含了另一个文件或目录的任意一个路径名。
在Linux公共文件模型下,目录和符号链接也是文件,只不过它们有不同的操作接口,或者有不同的操作实现。上层应用程序通过系统调用对文件或文件系统进行操作。Linux提供了open、read、write、mount等标准的系统调用接口。
Minix是Linux最早的文件系统。
三、文件数据存储方式
不同的文件系统有不同的文件存储和组织方式。以基于磁盘的文件系统为例,文件是以磁盘块为单位存储的,文件系统设计的一个重要问题是记录各个文件分别用到哪些磁盘块。基于磁盘的文件系统至少有以下几种文件数据存储方式。
3.1 连续存储
连续存储:即把每个文件作为连续数据块存储在磁盘上。这一方案简单、容易实现,记录文件用到的磁盘块仅需记住第一块的地址,并且性能较好,一次操作就可读出整个文件。但是,它有一个致命的缺陷。在创建文件时,必须知道文件的最大长度,否则无法确定需要为它保留多少磁盘空间。因此,连续存储主要适合于文件数据一次性写入的系统(例如romfs)。
3.2 链接表存储
链接表存储:即将每个文件使用的磁盘块顺序链接起来。虽然用于链接磁盘块的指针可以使用磁盘块本身的空间,但这将使得每个磁盘块实际存储的数据字节数不再是2的幂,给上层应用程序带来不便。文件系统从磁盘块中取出一些指针,作为索引存放在文件分配表FAT。只需要记录文件的起始磁盘块编号,顺着文件分配表中索引指针组织成的链表,就可以找到文件的所有磁盘块。
3.3 inode存储
inode存储:每个文件都有一张称为inode(索引节点)的表,通过它得到文件所有磁盘块的编号。小文件的所有磁盘块编号都直接存放在inode内。稍大的一些文件,inode记录了一个称为一次间接块的磁盘块编号,这个磁盘块存放着文件的其他磁盘块编号。如果文件再扩大,可以在inode中记录二次间接块编号,二次间接块存放着多个一次间接块的编号,而每个一次间接块又存放着文件的其他磁盘块编号。如果这也不够的话,还可以使用三次间接块。
四、通用块层
通用块层,其实是处在文件系统和磁盘驱动中间的一个块设备抽象层。Linux的通用块层(Generic Block Layer)是Linux内核中负责块设备(Block Device)管理和访问的子系统。它提供了一个抽象层,使得上层应用程序和文件系统可以统一地与底层块设备进行交互,而无需关心具体的硬件细节。
它主要有两个功能 。
文件系统
通用块层
块驱动/设备
第一个功能跟虚拟文件系统的功能类似。向上,为文件系统和应用程序,提供访问块设备的标准接口;向下,把各种异构的磁盘设备抽象为统一的块设备,并提供统一框架来管理这些设备的驱动程序。
第二个功能,通用块层还会给文件系统和应用程序发来的 I/O 请求排队,并通过重新排序、请求合并等方式,提高磁盘读写的效率。
其中,对 I/O 请求排序的过程,也就是 I/O 调度。事实上,Linux 内核支持四种I/O调度算法,分别是NONE、NOOP、CFQ以及DeadLine。
第一种 NONE ,更确切来说,并不能算 I/O 调度算法。因为它完全不使用任何I/O调度器,对文件系统和应用程序的I/O其实不做任何处理,常用在虚拟机中(此时磁盘I/O调度完全由物理机负责)。
第二种 NOOP ,是最简单的一种 I/O 调度算法。Noop算法是一种简单的FIFO(先进先出)调度算法。它不对I/O请求进行排序或调度,而是按照先后顺序将请求发送给块设备驱动程序。常用于 SSD 磁盘。
第三种 CFQ(Completely Fair Scheduler),也被称为完全公平调度器,是现在很多发行版的默认 I/O 调度器,它为每个进程维护了一个 I/O 调度队列,并按照时间片来均匀分布每个进程的 I/O 请求。CFQ算法旨在公平地分配磁盘I/O带宽给不同进程或任务。它将I/O请求放入多个队列中,并以时间片轮转的方式为每个队列分配磁盘访问时间。
类似于进程 CPU 调度,CFQ 还支持进程 I/O 的优先级调度,所以它适用于运行大量进程的系统,像是桌面环境、多媒体应用等。
最后一种 DeadLine 调度算法,Deadline算法基于截止时间来调度I/O请求。它尽量保证低延迟的实时响应,并根据请求的截止时间进行优先级排序,以确保及时完成对关键数据的读写操作。分别为读、写请求创建了不同的 I/O 队列,可以提高机械磁盘的吞吐量,并确保达到最终期限(deadline)的请求被优先处理。DeadLine 调度算法,多用在 I/O 压力比较重的场景,比如数据库等。
这些调度算法的选择可以通过在Linux系统中的块设备文件(例如/sys/block/<device>/queue/scheduler)中进行配置和更改:
# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq
通用块层的主要功能和特点包括:
(1)块设备抽象:通用块层提供了一个标准的接口,用于管理和访问各种块设备,如硬盘、固态磁盘、光盘等。不同类型的块设备可以通过通用块层进行统一的管理和访问。
(2)块设备驱动程序:通用块层通过块设备驱动程序与底层硬件进行通信。每种类型的块设备都需要相应的驱动程序来实现与通用块层的交互,并提供访问硬件设备的功能。
(3)块设备队列:通用块层使用块设备队列来管理和调度对块设备的读写请求。它可以对请求进行排序、合并和优化,以提高系统的性能和效率。
(4)缓存管理:通用块层提供了对块设备数据的缓存管理机制。它可以将数据缓存在内存中,以提高对块设备的读取效率,并减少对底层硬件的频繁访问。
(5)块设备层次结构:通用块层支持多层次的块设备结构。它可以将多个块设备组合成逻辑卷(Logical Volume),并提供对逻辑卷的管理和访问。这种层次结构可以方便地进行存储管理和扩展。
五、I/O栈
把Linux 存储系统的 I/O 栈,由上到下分为三个层次,分别是文件系统层、通用块层和设备层。这三个I/O层的关系如下图所示,这其实也是 Linux 存储系统的 I/O 栈全景图。
根据上面的 I/O 栈的全景图,我们可以更清楚地理解,存储系统 I/O 的工作原理:
(1)文件系统层,包括虚拟文件系统和其他各种文件系统的具体实现。它为上层的应用程序,提供标准的文件访问接口;对下会通过通用块层,来存储和管理磁盘数据。
(2)通用块层,包括块设备 I/O 队列和 I/O 调度器。它会对文件系统的 I/O 请求进行排队,再通过重新排序和请求合并,然后才要发送给下一级的设备层。
(3)设备层,包括存储设备和相应的驱动程序,负责最终物理设备的I/O操作。
存储系统的 I/O ,通常是整个系统中最慢的一环。所以, Linux 通过多种缓存机制来优化 I/O 效率。
比方说,为了优化文件访问的性能,会使用页缓存、索引节点缓存、目录项缓存等多种缓存机制,以减少对下层块设备的直接调用。
同样,为了优化块设备的访问效率,会使用缓冲区,来缓存块设备的数据。
参考资料
极客时间Linux性能优化实战
https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram