目录
一 文件系统是个什么玩意
二 常见文件系统的实现
1、Fat32文件系统
2、Unix文件系统
3、其他形形色色的各种文件系统
三 文件系统在存储介质(物理介质)上的映像
四 Linux下文件系统如何与内核挂钩
五 文件系统概念拓展
六 从设计层面考虑
文件系统,顾名思义,就是用来管理文件的。又因为文件都是存在于磁盘上的,所以,更进一步,文件系统又是用来管理磁盘上的文件的。文件本质上是有组织的数据,所以,再进一步,文件系统是用来管理磁盘上的数据的。
大部分普通用户对文件系统的直观感受,来自于磁盘的格式化操作,或者重装系统。在这两步操作中,都会提示用户选择文件系统的类型。许多人并不清楚,不同类型的文件系统有什么差别,做出的选择要么是默认配置项,要么是别人或者网络上的建议。在这个过程中,很多人并不关心文件系统的类型,只要保证所选择的文件系统类型不要影响自己后续的使用即可。
对程序员来讲,接触文件系统最多的应该就是平台提供的文件操作接口。包括创建文件、打开文件,向文件中写入内容或者读取文件里的内容。而在这个过程中,也只需要保证特定目录下的文件能成功的被读写即可。至于文件是如何存到磁盘上,又如何写到磁盘上,再又是如何从磁盘读出,则并不需要关心。
所以,不管是对普通用户还是程序设计人员来讲,看到的都是简化了的,逻辑化的文件系统,直观的表现就是基于盘符或目录的文件管理器。至于文件到底是在磁盘上如何存放的,都并不关心,也不需要了解。总的来讲,文件系统就是简化对磁盘的使用,方便用户以更加形象的文件方式存储各种数据。
但也正是因为如此,导致文件系统的功能作用比较单一,分层也比较清晰,虽然也有很多的细节和技巧技巧要掌握,但是相对于内存管理,它的实现逻辑还是要简单一点,也比较容易理解。不过在这一部分,我们不会讨论太多底层硬件相关的细节,而是更多的向读者展示文件系统的逻辑框架。
要更进一步了解文件系统是做什么的,到底是怎么工作的,还需要从一个实际例子入手。
基于操作系统的图形界面,我们看到文件系统最直接的表现,就是文件管理器。这也是我们直观感受文件系统最简单的方式。最顶层是磁盘,然后有目录,目录下有文件夹和文件。在文件这一级上,又有不同种类不同格式的文件。但是,不管是照片、视频还是各种可执行程序,也不管是什么格式的照片、视频和程序,在操作系统看来,统统都是文件。
上图为WIN10系统典型的文件管理器界面。
上图为Linux类系统Ubuntu发行版的文件管理界面。
上述两张图展现的文件系统,虽然底层实现大相径庭,但是面向用户所展现的概念还是比较接近的,都是磁盘、文件夹和文件。很显然,如果没有图形界面,那么就看不到形象的磁盘、目录、文件夹的图标,也看不到各种不同的文件格式对应的图标,有的只是字符串及斜杠组成的枯燥的"表达式",操作上也由图形界面的点鼠标方式,变成需要通过学习才能掌握的命令行。如下图所示:
命令行形式的文件管理界面
上面我们介绍了文件系统在直接面对用户的情况下的最直白表现,那么在文件系统的最低层,又是什么样子呢?在硬件模型部分,我们对磁盘进行了抽象建模,了解到磁盘实际的操作涉及到磁头、柱面、磁道、扇区等概念,而且有控制器来控制磁头的移动。要实现数据的读写,只要按照硬件要求,开发驱动即可。这里不考虑硬件细节,抽象一点,我们认为底层就是一块需要给定驻面、磁道以及扇区等参数,按块来读取的存储区。这样一来,底层就是在逻辑上由连续的小存储块构成的大块存储区,这与我们在抽象模型部分的介绍是一致的。
在了解了上层和底层以后,就可以清楚的看到,文件系统就是把底层连续的存储块组织成上层见到的,基于目录树结构的,方便用户使用的一套操作接口。
其实,图形界面傻瓜化了对文件系统的使用,同时也遮蔽了对底层细节的展露。相对而言,命令行界面反倒更贴近文件系统实际的接口,更易于理解文件系统。不过图形界面也是基于同一套接囗实现的,只是二者涉及的抽象层次不同。
下面我们用形象的语言来为上层与底层之间搭桥。我们假设用户单击了一个盘符,想看看这个盘下面,有哪些文件夹与文件。这一操作最终会转化为对操作系统文件系统模块的一个调用,比如就叫scandir。在这个调用里,只需传入路径参数,比如这里的盘符,接口就会按照文件系统的组织,查找目录和文件,然后返回路径下查找到的内容,比如这里需要的文件夹和文件的名称。在用户界面展示层,将文件夹和文件以不同的形象的图标进行显示,我们就可以看到路径下的东西了。这里的关键就在scandir接囗调用。在scandir接口中,应该实现这样的功能,通过路径参数,找到存储该路径下内容的磁盘块,将其中保存的文件夹和文件信息,按照特定的格式(文件系统相关的)进行解析,将解析后的文件和文件夹名称返回。这就是scandir接口的功能实现。
从上面的过程,我们看到,文件系统最简单的作用就是将磁盘存储块以特定格式组织起来,读写过程都遵守这个组织形式。这样,通过该组织形式,就可以将具体磁盘存储块与磁盘目录数据关联起来。这就好比是我们日常看的书,一页一页的内容,好比是磁盘块,而目录则是这种组织形式。通过目录,我们就可以方便的找到想要看的内容。如果要说二者有什么差别,最大的同在于书是静态的。一旦校订好,印刷完,所有就是定了的,包括目录是定了的,内容也是定了的,并且每一部分内容,都是连续存在的。但对于文件系统,则恰恰在于动。这个动来自于不仅可读,还要可写。这就是说,内容不一定是连续的,因为内容随时可增加删减;目录也不是固定的,内容中还可以嵌套更深的目录。文件系统的复杂性恰恰就在于既要通过目录来方便管理,又要满足这种动态变化带来的灵活性要求。下面我们就看两个常见的文件系统,来了解下实际使用的文件系统是怎么做到这种动的特性的。
知道了文件系统倒底是个什么玩意后,就来看看几个流行的成熟的文件系统,从不同的角度、层次,来一窥文件系统的实现奥妙。同时,通过对同一目标系统的不同实现的研究了解,也可加深对文件系统背后原理的理解。
Fat32是Windows系统上早期流行的文件系统。这里的32是指32位系统,因为Fat32是从最开始的Fat16系统演化发展而来的。在Fat32中,目录分级存放在扇区中表结构体中,因此整个结构相对而言很简单。如下图所示:
其中,FAT1为表结构,FAT2为FAT1的备份。可以理解表结构就是整个文件系统的“钢”,纲举目张,通过FAT1表,就可以知道有哪些目录,有哪些文件,都是什么属性,比如是可读还是可写,文件在哪里等等。因此,这种实现非常符合人们的理解习惯。跟书本的目录能够贴切的对应起来。
简单的结构虽然比较容易理解,容易实现,但是也容易造成灵活性和扩展性不足。所以,当下,Windows系统中,磁盘大都格式化为NTFS文件系统。但是FAT也并非一无是处,在嵌入式和一些小容量的SD卡等领域,还是在大量使用。
Unix基于树型结构来管理文件系统。下图为经典的Unix文件系统示意图。图片来自网络。
Unix文件系统的原理框图
这个架构中,最有特点的就是多级索引。直接块部分,记录了可直接获取的数据信息,对于特别小的文件,就不需要有一级和二级索引了。如果文件比较大,超出了直接块能够表示的范围,则可继续通过一级索引来获取数据。一级索引对应的块中保存的是数据的直接索引,所以,通过一级索引,获取到直接索引,再通过直接索引,就可以获取到数据。这有点C语言里面的指针的指针概念。自然,二级索引保存的就是一级索引,在通过其保存的一级索引查找到直接索引。显然,只有很大的文件,才需要用到二级甚至三级索引。
上面介绍的两个,只是目前比较典型的,适合教学(复杂度适中,原理清晰)的两个文件系统。并不是说主要的文件系统就这两个,还有许多形形色色的其他文件系统,并且主流的文件系统也已经不是上述这两种了。另外,还有被用于许多生活中所见到的消费电子产品上的一些文件系统,这尤以嵌入式系统多为常见。像Cramfs、Yaafs、JIFFS等基于Flash的文件系统,可能很多人都不曾听到过。这些文件系统是针对具体设备、具体应用场景设计的,各有自己的特点,也在不断的向前发展。像Yaafs是目前流行的智能手机安卓所用的文件系统。所以文件系统也不是越复杂越好,还是量体裁衣才好。其实还是常说的那句话,没有最好的,只有最合适的。这些非主流文件系统,在丰富多样的消费电子产品中广泛使用,正是因为它们满足了具体产品的需求,也具备足够的灵活性。
至此,我们了解了多个文件系统的实现原理。
不同的文件系统,有不同特点,比如有日志型的文件系统,可以再异常情况下,协助恢复部分数据;有只读文件系统,可以用于代码文件的存放;有压缩文件系统,可以压缩存储,节省空间;有网络文件系统,方便通过网络搭建和使用。对于不同格式文件系统的优缺点,网络上也有很多资料,感兴趣的读者可以搜索了解。
在前面,我们看了实际的文件系统后,再来思考一下,有没有什么特别的东西,是这些文件系统共有的特质?下面我们就探寻这个问题。
在看过几个文件系统后,在这一部分,我们来看看Linux早期版本上,文件系统的实现细节。这里就以Minux文件系统为例。在多本有关Linux早期版本实现原理的书籍中,对该文件系统都有过详细的讲解。在这里,我们更多的从内存和整体[就是从磁盘到内存的映像]的角度来谈讨其实现。
从本质上来讲,Minux文件系统是类Unix的文件系统,但是更为简单,细节也更少,确实很适合做文件系统的案例讲解,达到举一反三的效果。
首先,还是从必不可少的几个概念介绍开始。如下图所示:
上图展示了Minux文件系统涉及到的概念,包括:
超级块概念:超级块记录了文件系统的整体信息。比如是什么文件系统,分区的大小,分区的块数,mount挂载的相关信息,i节点的统计信息等。系统要挂载和读取一个文件系统,就从超级块开始。超级块就像是控制中心,通过超级块可以直接获取文件系统的总体信息,并可以通过一些间接指针,逐步的掌握该文件系统相关的所有信息。因此,被称作了超级块。上图是linux早期版本的实现方式,可以看到,在系统里使用一个超级块数组,用于保存所有挂载到系统的文件系统。
i节点概念:i节点的概念跟Unix文件系统中的i节点类似。内核通过i节点组织文件系统。无论是文件还是目录,最终都抽象到i节点上,由i节点最终找到具体的存储内容的磁盘块。内核中使用i节点位图表示所有的i节点使用情况。
逻辑块概念:这是最接近文件底层的物理概念。逻辑块用于实实在在保存文件内容。其实可以感受得到,当我们提到一个文件时,隐含的包含了文件本身的内容和文件的一些属性。这自然的就可以对应到逻辑块和i节点上。
对于内核来讲,除了上述物理层面组织文件的概念外,还有不少逻辑层面描述文件的东西,比如文件的描述符。文件是全局的概念,系统中每个打开的文件,都有相应的文件描述符指向。文件描述符是进程相关的概念,如果多个进程打开同一个文件,那么它们各自保存对文件的指向,也就是各自都有指向该文件的描述符。其中一个对文件的修改,最终也会被另一个观察到。
结合上面这些概念,我们来看看磁盘上格式化后的文件系统是什么样子。这个样子也就是我们使用文件系统之前,磁盘的状态。如下图所示:
文件系统格式化后磁盘的映像
有了磁盘映像,就有了基础。从上图也可以看出,格式化就是用于说明磁盘后续该如何使用。这个说明的内容,跟虚拟内存的页面类似,也是属于内耗,也就是其本身也存储在磁盘上。系统运行时,如果要挂载该磁盘,就会读取磁盘开始的内容,并按照文件系统的格式要求,解析相关内容,然后保存到内核数据区,构建一个动态的运行时结构。随着系统的运行,文件的创建和删除动作的进行,这个动态运行时的结构也会发生着变化。其实,我们可以想像出,工作一段时间后,磁盘的样子,它可能发生了如下一些变化:
1 磁盘空间占用情况在动态变化中;
2 i节点位图的空闲i节点情况在动态变化中;
3 逻辑区块位图中有关逻辑区块的使用情况在动态变化中;
4 i节点的关联关系在变化中;
5 最直接的,物理区块上有关文件的内容在动态变化中,从底层根目录到叶子文件。
下图是一个示例(图片来自网络)
感兴趣的读者,可以自己设想几个操作场景,然后将相关数据结构内容和关系的变化构图出来。这个变化最终反映到最终的物理磁盘上,这样断电后相关信息不会丢失。如果我们重新格式化了磁盘,自然,所有动态变化中产生的修改都将被抹平,一切都将回归到初始状态。这就是为什么使用格式化操作时要特别谨慎。
其实,如果读者刚才想像了在文件系统运行过程中,磁盘内容可能产生的变化,那么实际促使这个变化产生的过程,就是这一部分要介绍的文件系统在内存中的映像,而我们想的过程,就是CPU具体操作的过程。
有了磁盘映像后,内存映像并不复杂。因为,那些在磁盘映像中出现的数据块,在内存中都需要有相关的数据给构,以方便CPU的管理。这样,将这些需要用到的数据结构关联整合起来,就构成了文件系统的内存映像。如下图所示:(图片来自网络)
有了内存映像之后,就可以配合磁盘映像一起,展示Linux文件系统的整体工作过程了。
假设文件系统现在是处于格式化后的状态,还没有有效的目录和文件。首先,先创建一个目录,就叫TestDir,过程如下:
1 找到当前文件系统所在的超级块以及该超级块所在的挂载点。找到挂载点,也就找到了当前文件系统的“根”目录;
2 通过超级块找到一个空闲的i节点,用来创建目录;
3 通过超级块找到一个空闲的逻辑快,创建一个目录文件,该目录文件目前只有目录名;
4 将3中的逻辑块关联到2中的i节点上;
5 将2中的i节点关联到文件系统的根节点目录上;
6 同步内存和磁盘上的结构化数据。
第二步,我们在TestDir目录下创建一个文件,叫做TestFile,过程同上面类似,不过此时创建的是真正的实体文件对象。
通过上面两个流程的介绍,对整个文件系统的实现和工作过程,会有一个更加淸淅的认识。至此,算是解决了文件系统倒底是个什么玩意这个问题。此时,也就可以回答本节开始的问题----即文件系统共性的东西是什么了。个人理解,这个共性的点,本质上就是一种组织结构。同样的磁盘可以格式化为不同的文件系统,应用于同一个文件系统中,这中间的关联就是大家对磁盘和内存映像的组织结构达成了一致。不同的组织结构设计,就是不同的文件系统设计,自然也就有不同的复杂度和其自身的特点。
首先,通过上面的贴图,我们补充丰富了内存里面有关文件系统的内容。比如填加上文件系统相关的数据结构。部分内容可参考《Linux内核设计艺术》一书。
有了内核文件系统,那么内核文件系统与磁盘上的文件系统内容是怎么关联到一起的?因为CPU始终是在内存中执行逻辑,因此,即使是磁盘上的文件结构,也要与内存建立关系后,才可以发挥效果。在类Linux系统中,这种关联是通过挂载操作实现的。
文件系统的挂载概念,是存在于类Unix系统中的一个概念。因为类Unix系统的文件系统都采用了树型结构,这种结构在文件系统的可扩展性上,具有先天的巨大优势。挂载就是这一可灵活扩展的体现。挂载者,意为挂接扩展,其实也是一个形象的术语,其作用就如同下图的挂接过程:
显然,只要维护了上下级关系和根节点,从任何一个点出发,都是可用遍历整个树的。这也与我们文件系统中目录和文件的层级关系比较贴近。
上面这幅图形象的展示了挂载的含义。通过挂载,可以方便的实现对不同文件系统格式的外部存储的动态支持。通过挂载,也可以实现网络这种非实体存储的文件系统的支持。关于挂载所关联的非实体存储文件系统的相关概念,在拓展部分介绍,这里重点讨论基于实体存储的挂载实现流程。
通过上图我们初步了解了挂载的含义,目的。挂载作为操作系统中与文件系统相关的一个非常重要的基本操作,具体是如何实现的呢?后面,我们将详细的介绍这一过程。其实挂载过程并不复杂。
首先,我们看一下Linux下,挂载的具体命令是什么。通过命令,间接对挂载有一个接近实现的认识。
mount -t (xxx) /dev/sd[x] /root/mnt/
一个最简单的挂载命令如上。T选项用于指明挂载时采用的文件系统;dev目录下对应的是具体的设备。最后是要挂载到的位置。也就是将某个设备,按照某个文件系统挂载到现有文件系统的某个目录下。有时候,文件系统类型不是必须的,系统会自动解读设备上的文件系统类型。所以,最关键的两个参数就是将那个设备挂载到那个目录下。
其次,我们需要明确,一种文件系统类型是一个相对封闭的小系统,这里说封闭,是说文件系统自身的信息,足以满足该文件系统相关的所有操作,不再需要额外的辅助信息,这就为挂载的实现,提供了可能和便利。更直白的描述就是,操作系统内核中有一些数据结构,保存了文件系统的关键信息,包括节点信息,系统表等。依据这些信息,内核就可以很容易掌握整个文件系统的信息,这些节点就像是纲,起提纲挈领的作用。到此,读者可能隐约的感觉到了,对于我们之前介绍的minux文件系统,通过超级块,及相关辅助数据结构,就可以对文件系统包括的东西,有完整的掌握。超级块就像是一本书的最顶层的目录,即使所要寻找的内容隐藏很深,也能通过目录,顺藤摸瓜,一级接一级,最终找到需要的内容。
注意,上面的描述有一个隐含的信息,那就是超级块的位置是确定的。指定设备参数,就等于间接告诉内核,超级块在哪里了。
有了上述两条内容建立的基本概念后,这里以Linux内核为例,来介绍Linux内核挂载根文件系统的过程。
1 内核为什么挂根文件系统
我们先看看什么是根文件系统。根文件系统也是一个文件系统,但是与一个单纯的文件系统不同在于,根文件系统中集成了系统运作的必要程序。内核完成自己的工作后,挂载根文件系统,然后执行根文件系统中的init程序,并将交互控制交给该程序。基于该程序,我们可以实现各种不同的应用场景。最普通、最普遍的就是调出命令行,也就是我们常说的shell环境,然后等待用户使用系统。
内核要跑起来,必须有根文件系统的支持吗?从能力来讲,这个要求不见得是必须的。内核完全可以设计成这样:启动后构建一个基于内存的最小系统,然后等待用户的使用。这个基于内存的最小系统,不需要其他辅助条件,内核自己就可以搞定。但是,从设计的角度来看,从分工的实现来看,将根文件系统和内核分开,有助于内核专注自身的进化,也有助于支持多种多样的系统。
这就好比,内核设计的可以执行程序,但是只需要约定好二进制格式就可以了,没必要内核自己去实现各种程序。我们可以认为,不同的发行版,就是不同的根文件系统。内核是系统的核心,但不是系统的全部。围绕内核构建的一个小生态,才是我们传统意义上说的系统,面向用户的系统。包括Windows、Ubuntu、Android、iOS等等,都是从这个角度来表达操作系统概念的。
2 内核挂根文件系统前的准备工作
参考《Linux内核设计艺术》
3 内核挂根文件系统执行的命令
《Linux内核设计艺术》
4 命令执行的过程
《Linux内核设计艺术》
5 挂载完成后内存中的内核情景图
参考上面的文件系统内存与磁盘映像图。
好了,至此,对文件系统的挂载基本就介绍完了。
通过上面介绍,可以对挂载的条件做一个总结梳理:一,要挂载文件系统所在存储设备;二,要挂载文件系统的类型;三,新的文件系统要挂载到的目的地,也就是目的目录。满足了这三条,整个挂载过程就是,从源设备上找到文件系统的“钢”,经过一系列的处理,挂接到目的目录上。后续用户操作目的目录时,即按照新挂接的文件系统类型规则进行,用户看到的也是新设备文件系统中的内容。但是具体的操作或者说展示给用户的视图,并不改变,仍然是基础的文件系统层次结构,而且用户感觉不到文件系统的切换过程。
到目前为止,我们更多介绍的,还是传统意义上的文件系统。其实在计算机技术发展的很早期,一些非传统意义上的文件系统就已经存在,并且很流行了,像网络文件系统等。这就是我们这一部分要介绍的文件系统概念的拓展。
由于文件系统的操作接口相对已经很成熟了,且又存在大量的文件系统类型(且还不断有新的文件系统类型研发出来),并且还有非传统意义上的文件系统存在,那么该如何应对这各种各样的文件系统类型呢?显然,得做很好的抽象,才能应对未知的变化。操作系统的设计者就是这样做的。这跟面向对象设计里的抽象思想是一致的。操作系统的设计者们也设计了一个抽象文件系统(在linux里,这个文件系统叫虚拟文件系统VFS),定义了文件系统应有的接口,然后在这套接口的约束下,可以有各种具体的文件系统设计与实现。最终,形成了类似如下图所示的文件系统架构(图片来自网络):
从上图可以看出,这种架构中,包括了一个虚拟文件系统层,向上,屏蔽底层各种不同文件系统类型及接囗差异,向用户提供统一的操作接口;向下,为不同文件系统提供统一的对接接口,文件系统的实现者可以根据需求,设计自己的文件系统,最终只要正确对接到虚拟层的接口上即可。
通过这种分层的设计,既达到了简化文件系统架构的目的,同时又满足了灵活性和可扩展性的要求。相当于搭好了一个框框,里面的内容可根据需求灵活选择。对于Linux操作系统而言,框框里具体文件系统类型的选择,既可以是动态的,也可以是静态的。这个动静态怎么讲呢?在编译内核时,可以选择将某个文件系统编译到内核代码中,也可以根据磁盘存储空间大小,选择不将某些文件系统编译到内核代码段和数据段中,从而达到减少磁盘空间占用的目的。这就是静态的选择。另外,文件系统也可以被设计为动态库的形式,在运行过程中根据需求,动态加载到内核中,以支持特定的存储设备。这种可在操作系统运行过程中根据需要来支持某种文件系统的方式,就是动态的选择。Linux内核通过动静结合,可以满足多样的需求,尤其是在嵌入式领域。
在前面,我们提到了传统意义上的文件系统,还有非传统意义上的文件系统。这里所谓的传统意义上的文件系统,多指最终面对各种实际存储设备的文件系统,比如磁盘,U盘等。而非传统意义上的文件系统,则多指不直接面对实际存储设备的文件系统,像前面提到的网络文件系统就是这种。那内核是如何支持这类非传统意义上的文件系统的呢?
其实道理是一样的。这里的关键就在于如何定义文件系统的各种接口行为。比如,以Unix中一切皆文件的抽象思想为例,网络设备也可以看做是一个文件。打开网络设备时,可以完成socket的创建和连接过程。读网络文件设备时,就对应到socket的读上,写对应到socket的写上,这样我们就可以将网络设备关联到文件上。网络文件系统也是类似道理。虽然网络文件系统不对应本机实际的存储设备,但是往往会对应到目标节点的物理存储设备,网络接口只是中间的一个代理而已。这样,我们就可以将文件系统的相关操作通过网络转发,自然就可以实现基于网络的文件系统了。
至此,我们对文件系统的概念有了一些介绍。当然,这其中,有的部分详细,有的部分粗略。在这一节,要考虑的是,当我们要自己动手设计一个完善的文件系统时,应该考虑些什么?看了、学了别人设计的文件系统,我们能不能也设计一个有特点的文件系统岀来?这与其说是一个问题,还不如说是一个总结。因为将别人设计过程中考虑的共性问题总结出来,答案也许就自然而然明了了。关于这个主题,是一个开放的话题,这里粗略总结如下一些内容,权当抛砖引玉用。
1 读写原则:只读、读写。显然,如果是一个只读文件系统,就可以少考虑很多问题,从而简化设计,将焦点关注于文件系统的其他特性上,比如更少的内存占用,更高的吞吐率等。推而广之,还可能存在是否修改的特性。比如对于大数据应用场景,常常是写完数据后,后续主要是查询,而很少存在修改的操作,针对这种场景,也需要对文件系统的设计做出相应的改变。
2 节点原则:节点占用尽量少的内存空间。这一方面可以节省空间,另一方面也可能加快加载速度。当然,这不是绝对的。对于现在的系统,还要考虑CPU访问总线的宽度、cache的大小、缓存的设计等。其实,这一点可以更准确的总结为更高效的节点设计。
3 速度原则:高速。这是一个很重要的指标。如何在特定的存储设备上优化设计,达到高的读写速度,是特别体现综合设计能力的。
4 文件大小:比如是否支持超大文件等。这会影响节点的组织和层级设计。
5 压缩原则:是否可压缩。这里说得压缩,不是说是否支持压缩文件,而是文件系统自身是否支持压缩。这种情况大多可能出现在嵌入式系统上面,应用于存储空间较少的情况。显然,压缩会影响速度。
6 日志原则:是否带有日志信息。这一点主要是考虑是否需要对文件进行恢复。当然,任何特性都可能是需要付出代价的。比如,这里的恢复,就需要冗余设计,需要占用额外的存储空间等。
7 缓存原则:通常来讲,CPU内部的寄存器访问在纳秒级别,内存访问通常在几十到百纳秒内,而一旦访问物理磁盘,则可能在几十毫秒,所以,为了优化磁盘访问,提高文件读写效率,常常需要在内存中提供缓存,用于保存预读的磁盘内容或者预写到磁盘的内容。
8 备份原则:如前第一条提到的,针对大数据场景的文件系统设计,就有很高的可靠性要求。这种场景下,往往需要将数据写入多个物理无关的磁盘区块,从而在某些物理硬盘损坏时,仍然能够保证数据的完整性。
9 索引原则:这主要是针对快速检索的需求。类似数据库查询的设计实现。
10 均衡原则:对于flash这类设备,读寿命远远大于写寿命,这是因为写操作通常都需要关联一个擦除操作。针对这类设备,实现文件系统时,就需要考虑均衡性,也就是写数据所在的区块,要整体上相对均衡一些,避免出现热点区块,导致坏块的出现,从而整体延长设备的使用寿命。另外,像一些日志型的文件系统,也不太适合这种设备,因为大量的日志写操作,会明显影响设备的使用寿命。
11 随机原则:这也是与物理存储设备相关的一个特性。有的设备支持随机的读写,而有的类型的设备对随机读写支持的不是很好。这样,在设计文件系统时,就需要考虑这个问题,有针对性的进行优化设计。
从上面这些原则可以看出,有些之间是相互冲突的,这也就解释了为啥会有这么多种类的文件系统了。我们很难在一个文件系统中包含所有的特性,据此,我们可以说没有最好的文件系统,只能说是最合适的文件系统。
以上为个人的一点总结,仅供参考。其实对于这个话题的深入思考和讨论,对理解现有的各种文件系统也是很有帮助的。这就像做题,如果是依赖答案,而不是自己深入思考做出来的,下次遇到仍然可能做不出来。思考就是内化的过程,这也是为啥要总结这一节的原因。