2023-06-11
原文作者:奇小葩 原文地址:https://blog.csdn.net/u012489236/category_10976532.html

当Unix操作系统首次引入时,使用了minix文件系统,它非常简单,基本上,它的数据结构在磁盘上看起来像是这样:

202306111257486531.png

超级块包括了有关整个文件系统的信息,卷大小,有多少inode,有多少空闲块等等,磁盘的inode区域包括文件系统的所有inode,最后,大部分磁盘都被整个数据块占用。

  • 对于非常常见的读操作,每当第一次读取inode,然后读取文件的数据块时,就会导致寻道时间很长
  • 更糟糕的是,文件系统最终会变得非常的碎片化,该设计没有对空闲空间的管理,结果导致在磁盘上来回访问逻辑上连续的文件,从而大大的降低了性能

例如,假设一下数据块区域包括4个文件(A/B/C/D),每个文件大小为2个块:

202306111257492462.png

如果删去B和D,则生成的布局为:

202306111257501953.png

可见空间被隔离成两块区域,而不是很好的连续4块,假设我们希望分配一个大小为4块的文件E:

202306111257509014.png

我们可以看到E被分散在磁盘上,因此,在访问E时,无法从连续获取,首先先读取E1和E2,然后寻道,再读取E3和E4,这个碎片问题就会影响性能问题。

对于这个问题,正式磁盘碎片整理工具可以解决的,它们将要重新组织磁盘数据,以连续放置文件,并且让空闲称为一个或几个连续的区域,移动数据,然后重写inode等以反映变化。

所以,如何组织文件系统数据结构,以提高性能?在这些数据结构之上,需要哪些类型的分配策略?如何让文件系统具有磁盘意识等,就成为改进文件系统的重中之重。

1 FFS文件系统

FFS(Fast File System),诞生于80年代的一款文件系统,对其前任进行了大量的性能优化,成为了现代类*nix文件系统的始祖,其很多设计思想在今天依然有借鉴意义。

设计思想:

1.1 提高block大小

FFS之前的文件系统块大小基本定义为512或1024byte,该方案的利弊

优势 降低block内的碎片率,提供了存储空间利用率
劣势1 数据传输以block为单位,block较小可能导致传输效率较低,此时寻道时间占据更大的比重,进一步恶化了磁盘IO效率
劣势2 由于古老的文件系统采用是直接+N级间接索引来定位文件,较小的block大小可能导致需要的索引块会更多,降低了文件IO效率

在文件普遍偏小的当时,采用较小块劣势没那么明显。但是随着应用发生变化,大文件场景越来愈多,如果采用更大的block,那么其优劣势正好与较小的块大小互换。明显地提升磁盘IO效率的同时可能也会带来较大的空间浪费(block内碎片)。

FFS激进地提升了文件系统块大小至4096字节,且block size可作为参数写入文件系统超级块。即意味着,块大小可定制,一般是4096乘以2的N次方,但该参数一旦固定,便不可再修改,除非重新格式化磁盘。

1.2 柱面组

FFS将整个磁盘/磁盘分区划分成多个称为cylinder group的区域。每个cylinder group由多个连续的cylinder组成。而一些现代文件系统,如linux ext2和ext3,就称为块组,即block group,因此我们可以想象一个具有10个柱面组的磁盘:

202306111257518365.png

cylinder的特点是在多个cylinder之间切换只需切换磁头而已,无需机械运动,这对于我们后面组织数据,提升数据IO效率有较大帮助。这些分组是FFS改善性能的核心机制,通过在同一组中放置两个文件,FFS就可以确保先后访问两个文件不会导致穿越磁盘的长时间寻道。

因此,FFS需要能够在每个组中分配文件和目录,每个组看起来是这样的:

202306111257525236.png

在每个组中,我们需要记录该组的inode和数据块是否已经分配,每个组的inode位图(inode bitmap)和数据位图(data bitmap)起到了这个作用。最后,inode和数据区就像之前文件一样。

1.3 策略:如何分配文件和目录

有了这个分组结构,FFS现在就需要如何在磁盘上放置文件和目录以及相关的元数据,以提高性能。

因此FFS必须决定什么是“相关的”,并将它们置于同一个区块内,相反,不相关的东西应该放在不同的组内。为了实现这一目标,FFS使用了一些简单的防止推导方法:

  • 首先是目录的放置,FFS采用一种简单的方法,找到分配数量少的柱面组和大量的自由Inode,因为我们希望随后能够分配一堆文件,并将目录数据和inode放在改组中
  • 对于文件,首先需要却道将文件的数据块分配到其inode相同的组中,从而防止inode和数据之间的长时间寻道;其次,它将位于同一目录中的所有文件,都放到它们所在目录的柱面中

一次典型的文件读取包括:

  1. 从磁盘读取文件目录项;
  2. 从磁盘读取文件inode;
  3. 从磁盘读取数据块。

最差情况下可能得经历3次IO,如果是随机IO,会导致文件访问效率急剧下降。为了将文件读取的随机IO变成顺序IO,我们就要尽可能地将文件的元数据(目录项、inode)以及数据块连续存放。在FFS内:

    1. 将磁盘划分成Cylinder group,这个我们在前面描述过。文件的数据和元数据尽量位于相同group存放;
    1. 创建文件时,将文件inode与其父目录的inode位于同一个cylinder group,这样对父目录进行ls时可提高效率;
    1. 创建目录时选择inode策略与创建inode不同:选择一个空闲inode数高于平均值且目录数最少的group。这样做可以使得目录被spread over到所有的cylinder group;
    1. 数据块分配时,对于低于阈值(48KB)的文件,其block分配在与文件inode相同的cylinder group,以后每涨1MB切换一个cylinder group。选择新cylinder group的准则是其拥有空闲block数量高于平均值。

2 总结

FFS的引入时文件系统的一个分水岭,因为它清楚表明文件管理问题是操作系统的一个大问题,并展示了如何开始处理硬盘,针对前任的缺点提出更加高效解决方案,较好地解决了文件系统的IO效率低下问题,属于现代文件系统的开山之作。从此之后,人们开发了数百个新的文件系统,现在仍然有许多文件系统从FFS中获得提示(ext2/ext3)。

阅读全文