1磁盘是怎样工作的?
第一类,机械磁盘,也称为硬盘驱动器(Hard Disk Driver),通常缩写为 HDD。磁盘有多个盘片,每个盘片双面存储。磁道(Track)磁头(Head)在盘片上画出的不同半径的同心圆。柱面(Cylinder)全部盘片相同磁道组成的圆柱侧面。柱面是从0开始编号,由外向内。柱面越靠外,吞吐量越大。(因为越靠外转动的线速度越大。扇区(Sector)盘片上的扇形区域。每个扇区512字节。是硬盘的基本单位。从1开始编号。每个扇区中的数据作为一个单元同时读出或写入。硬盘的0柱面0磁头1扇区是系统启动时首先读取的扇区。
第二类,固态磁盘(Solid State Disk),通常缩写为 SSD,由固态电子元器件组成。固态磁盘不需要磁道寻址,所以,不管是连续 I/O,还是随机 I/O 的性能,都比机械磁盘要好得多。
2磁盘逻辑结构概观
磁盘读写的最小单位是扇区,然而扇区只有 512B 大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统Ext又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB,也就是由连续的 8 个扇区组成。a)如果是一个启动盘,我们需要预留一块区域作为引导区,所以第一个块组的前面要留 1K,用于启动引导区。b)磁盘在执行文件系统格式化时,会被分成三个存储区域,超级块、索引节点区和数据块区。
- 索引节点:简称为 inode,用来记录文件的元数据,比如 inode 编号、文件大小、访问权限、修改日期、数据的位置等。索引节点和文件一一对应,它跟文件内容一样,都会被持久化存储到磁盘中。所以记住,索引节点同样占用磁盘空间。
- 数据块:简称为block,普通文件用来记录文件的数据。目录文件的块里面保存的是目录里面一项一项的文件信息,每一项都会保存这个目录的下一级的文件的文件名和对应的 inode,通过这个 inode,就能找到真正的文件。第一项是“.”,表示当前目录,第二项是“…”,表示上一级目录,接下来就是一项一项的文件名和 inode。
- 超级块:简称为super_block。用于存储文件系统自身元数据的核心结构。其中的信息包括空闲和以使用块的数目、块长度、当前文件系统的状态、各种时间戳。还包括一个表示文件系统类型的魔数,能检查mount确认文件系统的类型是否正确。一般只使用块组0中的超级块,因此也叫主超级块。如果主超级块信息损坏,可以从超级块的备份信息中复制数据来修复。c)另外还有几个概念了解一下:
- 组描述符表(GDT):组描述符表包含的信息反映了文件系统中各个块组的状态。其中信息包含块组中空闲块和空闲inode的数目。每个块组都包含了文件系统中所有块组的组描述符信息。由于GDT对于定位文件系统的元数据非常重要,因此和超级块一样,也对其进行了备份。GDT及其备份的内容都是一样的,所占块数也相同。
- inode位图:用二进制的方式记录了inode的使用情况, 比如inode是否空闲等。
- 数据块位图:用二进制方式记录了块的使用情况。当查找或创建文件时,会扫描此位图来寻找空闲的inode号对应的块。一个块组可以有32768(40968)个逻辑块。每一个块组均有自己的块位图与inode位图,用于记录本块组中块与inode的使用情况。一个逻辑块的大小为4K。则一个块位图所描述的块组最大为40968*4K=128MB。
- inode列表:包含了块组中所有的inode,inode用于保存文件系统中与各个文件和目录相连的所有元数据。
重要数据备份
默认情况下,超级块和块组描述符表都有副本保存在每一个块组里面。如果开启了 sparse_super(稀疏超级快) 特性,超级块和块组描述符表的副本只会保存在块组索引为 0、3、5、7 的整数幂里。除了块组 0 中存在一个超级块外,在块组 1(30=1)的第一个块中存在一个副本;在块组 3(31=3)、块组 5(51=5)、块组 7(71=7)、块组 9(32=9)、块组 25(52=25)、块组 27(33=27)的第一个 block 处也存在一个副本。对于超级块来讲,由于超级块不是很大,所以就算我们备份多了也没有太多问题。但是,对于块组描述符表来讲,如果每个块组里面都保存一份完整的块组描述符表,一方面很浪费空间;另一个方面,由于一个块组最大 128M,而块组描述符表里面有多少项,这就限制了有多少个块组,128M * 块组的总数目是整个文件系统的大小,就被限制住了。
这样会产生一个限制,以Ext4的块组描述符大小64 Bytes计算,文件系统中最多只能有2^21个块组,也就是文件系统最大为256TB。
Flexible Block Groups
这是ext4引入的一个特点。就是将连续的多个block groups绑在一起组成一个逻辑块组,称之为flex_group。在一个flex_group中,第一个物理block group是存放当前flex_group全部的bitmap、inode表。也就是说将几个块组合并为一个更大的块组。比如flex_group的大小为4(就是由4个块组组成),其中的group0将按顺序存放Super Block、GDT、4个块组的块位图、4个块组的inode位图、4个块组的inode表,剩余的空间是用作数据块。就是说ext4将几个块组合并为一个更大的块组。
flex_group块组的作用是:
- 聚集元数据,加速元数据载入
- 使得大文件在磁盘上尽量连续 即使开启flex_bg特性,超级块和块组描述符的冗余备份仍然位于块组的开头。Flex_bg中块组的个数由2^ext4_super_block.s_log_groups_per_flex 给出。这是为了减少磁盘寻道操作,将频繁访问的块组资源放在连续空间上。同时也能一次申请更多的块;因为一次性申请的块最大数目是一个组的块数。
Ext4引入Meta Block Groups
首先,块组描述符表不会保存所有块组的描述符了,而是将块组分成多个组,我们称为元块组(Meta Block Group)。每个元块组里面的块组描述符表仅仅包括自己的,一个元块组包含 64 个块组,这样一个元块组中的块组描述符表最多 64 项。我们假设一共有 256 个块组,原来是一个整的块组描述符表,里面有 256 项,要备份就全备份,现在分成 4 个元块组,每个元块组里面的块组描述符表就只有 64 项了,这就小多了,而且四个元块组自己备份自己的。
根据图中,每一个元块组包含 64 个块组,块组描述符表也是 64 项,备份三份,在元块组的第一个,第二个和最后一个块组的开始处。这样化整为零,我们就可以发挥出 ext4 的 48 位块寻址的优势了,在超级块 ext4_super_block 的定义中,我们可以看到块寻址分为高位和低位,均为 32 位,其中有用的是 48 位,2^48 个块是 1EB,足够用了。
struct ext4_super_block { ...... __le32 s_blocks_count_lo; /* Blocks count */ __le32 s_r_blocks_count_lo; /* Reserved blocks count */ __le32 s_free_blocks_count_lo; /* Free blocks count */ ...... __le32 s_blocks_count_hi; /* Blocks count */ __le32 s_r_blocks_count_hi; /* Reserved blocks count */ __le32 s_free_blocks_count_hi; /* Free blocks count */ ...... }
3数据块和Inode分配策略
在机械磁盘上,保持相关的数据块相互接近可以总的磁头移动时间,因而可以加速磁盘IO。在SSD上虽然没有磁头转动,数据局部性可以增加每次IO请求的传输的数据大小,因而减少响应IO请求的传输次数。数据的局部性对单个擦除块的写入产生影响,可以加速文件重写的速度。因而尽可能减少碎片是必要的。inode和数据块的分配策略可以保证数据的局部集中。以下为inode和数据块的分配策略:
- 多块分配可以减少磁盘碎片。当文件初次创建的时候,块分配器预测性地分配8KB的磁盘空间给文件。当文件关闭的时候,未使用的空间当然也就释放了。但是如果推测是正确的,那么文件数据将写到一个多个块的extent中。
- 延迟分配。当一个文件需要更多的数据块引起写操作时,文件系统推迟决定新数据在磁盘上的存放位置,直到脏的buffer写到磁盘为止。
- 尽量保持文件的数据块与其inode在同一个块组中。可以减少磁盘寻道时间.
- 尽量保持同一个目录中的所有inodes与目录位于同一个块组中。这样的假设前提是一个目录中的文件是相关的。
- 磁盘卷被分成128MB的块组。当在根目录中创建目录时,inode分配器扫描块组并将新目录放到它找到的使用负荷最小的块组中。这可以保证目录在磁盘上的分散性。
- 即使上述机制无效,仍然可以使用e4defrag整理碎片文件。
4硬链接与软链接
a)硬链接:
- 多个文件指向同一个inode,这些文件的inode number相同
- 硬链接表明文件可以通过不同的文件名访问
- 不能对目录创建硬链接
- 硬链接不能跨分区
- 每多一个硬链接,inode的引用计数(链接数)+1 b)软链接(符号链接):
- 文件及其软链接文件,使用的不是同一个inode,inode number不一样;
- 软链接文件的实际数据是另一个文件的路径,是一个字符串,软链接文件的大小为该字符串的长度;
- 可以对目录创建软链接
- 软链接可以跨分区
- 增加文件软链接,不会增加inode的引用计数