分享

彻底搞懂文件系统

 Fengsq501u81r4 2021-06-14

什么是文件?

我们假设将存储设备像切豆腐似的切成一个个的物理块,每个物理块可以是512字节,1KB,2KB,甚至64KB,这个物理块的大小是存储设备最小存储单元的整数倍,例如磁盘的最小存储单元是扇区,它通常大小为512字节,那么物理块大小就是512字节的整数倍,物理块设置为多大取决于文件系统类型和实际场景,这个没有绝对,我们只需要假设存储设备是由一个个物理块组成就可以了,另外存储设备可以是内存,磁盘,磁带,固态硬盘等,很多场景下是磁盘,现在很多个人电脑都采用了固态硬盘,这个不重要,只要存储设备可以分成一个个物理块就行,如下图所示为存储设备的物理块

彻底搞懂文件系统

物理块

上图所示的存储设备包括32个物理块,实际情况下存储设备的物理块远远大于这个数,例如有1亿个甚至几十亿个。

为了屏蔽底层各类存储设备的不同特性,也是为了计算机用户能够从人类的角度方便使用这些存储设备,计算机设计人员对存储设备的进行了高度抽象,就发明了文件,文件可以认为是逻辑存储设备,人们使用文件不需要考虑底层的存储设备是什么和底层是如何存储的,只需要通过文件路径交由文件系统处理就可以了,当然人们使用存储设备时,也可以抛弃文件系统,直接访问物理块,只不过大多数情况下没有必要这么做罢了。

文件结构

文件从物理的角度来讲:

它是由一个或者多个物理块构成,一个文件的大小不一定是物理块的整数倍,比如一个文件只有10字节,一个物理块的大小为1KB,那么这个文件就需要占用一个物理块,哪怕它的大小没有一个物理块大,这样就有1014个字节被浪费,再比如一个文件大小为2049个字节,物理块的大小为1KB,那么这个文件占用3个物理块,2个物理块用满了,还剩下一个物理块用于存储剩下的1个字节,有1023个字节浪费了,这样看,文件或多或少都会有些碎片,这种碎片叫做内部碎片,这个是无法避免的,可以通过统计分析的方法将物理块设置为合适的大小,从文件系统整体角度减少内部碎片。

文件从逻辑的角度来讲:

它是由一个个逻辑记录构成,逻辑记录的长度有固定和非固定两种,文件采用什么数据结构组织这些逻辑记录各有不同。

有3种常用的方式。

第一种:

对于unix,linux,windows这些操作系统,它们视文件是一个无结构的字节流的,也就是每个逻辑记录就是一个字节,对于这些操作系统来说,它不知道文件的内容是什么,文件内容是什么还是要由能够解析它的程序去解释,如下图所示

彻底搞懂文件系统

字节流

第二种:

有的文件由固定长度的且大于1的逻辑记录构成,可以认为这类文件是一个逻辑记录序列,这类文件的读写都是按照逻辑记录作为最小单位的,如下图所示

彻底搞懂文件系统

固定长度的逻辑记录

第三种:

还有一种文件是一颗树,这颗树的每个节点是一个逻辑记录,但是这个逻辑记录的长度不是固定的,每个逻辑记录都有一个唯一键,整颗树就是按照这个键进行排序,因此这类文件容易根据键来定位到逻辑记录,这类文件一般用于数据处理系统例如数据库,如下图所示

彻底搞懂文件系统

树形结构的逻辑记录

上图的蓝色部分就是唯一键,黄色部分为非固定长度的逻辑记录。

综上所述,一个文件的写入过程就是将逻辑记录打包成一个或者多个物理块,一个文件的读入过程就是将一个或者多个物理块解包成逻辑记录的过程。

文件类型

文件有很多种类型,比如源文件,目标文件,可执行文件,文本文件,文字处理文件,视频文件,音频文件,图像文件,归档类文件,库文件等,这些文件的逻辑记录结构可以是字节流也可以其它结构,无论其结构如何,都需要有专门的软件去分析这些文件,所以文件类型是相对于能分析它的软件来说的。

操作系统通常支持可执行文件类型,不同的操作系统可执行文件的格式可能不同,操作系统可以识别可执行文件,将可执行文件加载到内存中去运行,其他的文件类型操作系统就不能保证了,这个可以由专门的用户软件去处理,例如word类型的文件有word处理软件去处理。

有的文件类型可以根据文件开头的幻数来表明,例如class文件,部分可执行文件,但不是所有文件类型都有幻数,很多操作系统的完整文件名由文件名加后缀来表示,例如x.exe表示一个windows可执行文件,y.jpg表示一个图像文件,当然这个后缀名只是提示用户这个文件的类型是什么,操作系统不能保证文件的真实类型是不是与后缀名一致。

文件属性:

所有的文件都有些共同属性:

文件名称:

就好像人有名称一样,文件也有名称,这个名称在当前目录下是唯一的,通常是人类易读的,有的操作系统对名称区分大小写。

标识符:

它是一个数字,是文件系统内唯一的,它唯一标识一个的文件,它不是人类易读的,文件系统能读懂它就可以了。

类型:

如上文所述的文件类型

大小:

文件当前的大小,通常以字节为单位,可以转化为人类易读的KB,MB等。

权限:

通常文件有可执行,可读,可写权限,这个属性决定用户如果想要访问这个文件,必须有的最低权限。

创建时间,创建人:

文件创建的时间和创建文件的用户的标识。

修改时间,修改人:

文件最近修改的世界和修改文件的用户的标识。

除了以上的共同文件属性外,不同的操作系统可以有各自的扩展属性。

所有的文件属性存储在存储设备上,例如存储在硬盘的某个区域,具体存储在哪个区域,怎么存储,不同的文件系统不太一样。

文件操作:

一个文件支持6种基本操作

创建文件:

分2步创建一个文件

一:由文件系统为文件分配一个物理空间即一个或者多个物理块。

二:在目录下创建一个目录条目,具体什么是目录和目录条目,后续再介绍。

写文件:

首先系统根据文件名称在目录中搜索,找到目录下的目录条目,根据目录条目找到文件的物理位置,然后写入文件内容到文件中。

读文件:

首先系统根据文件名称在目录中搜索,找到目录下的目录条目,根据目录条目找到文件的物理位置,然后读取物理位置处的内容。

重定位文件:

首先系统根据文件名称在目录中搜索,找到目录下的目录条目,然后跳转到文件的指定位置,之后就可以在指定的位置处进行读写数据。

删除文件:

首先系统根据文件名称在目录中搜索,找到目录下的目录条目,根据目录条目删除该目录条目对应的文件,并释放文件占用的空间,同时删除目录条目。

截断文件:

首先系统根据文件名称在目录中搜索,找到目录下的目录条目,根据目录条目将该目录条目对应的文件的长度设置为0并释放空间,但文件属性和目录条目不会删除。

上述6个文件操作为文件的基本操作,其它复杂的文件操作可以由上述6个操作任意组合来实现。

上述6个文件操作中,除了create,都要从存储设备中搜索目录条目,频繁地搜索目录条目很影响效率,因此通常文件系统会啊将目录进行缓存。

文件锁:

文件锁分为共享锁和专用锁。

共享锁表示多个进程可以并发获取,不会产生互斥,主要用于读取文件信息,专用锁表示只有一个进程可以获取,是互斥的,主要用于写数据信息,不是所有的操作系统都支持这两种锁,有的操作系统只支持专用锁。

文件访问:

顺序访问:

从指定的文件位置(一般从头开始)开始顺序读入或者写入数据,这些数据物理上是挨着的,比如拿磁盘来说,写完某个扇区的数据后,紧接着写下一个扇区,直到一个磁道写完,然后再写相邻的磁道或者同一个柱面的上下移动磁头写到另外一个盘面的同一个磁道,因此顺序访问的访问速度是最快的,毕竟不会频繁地寻道。

随机访问:

用户可以随机指定一个物理块,然后读入或者写入这个物理块,例如我们把每个班级的学生记录存储到一个物理块上,然后如果我们想要获取某个班级的学生记录,可以班级号采用哈希函数映射到一个物理块,然后直接访问这个物理块。

随机访问通常需要频繁地寻道,毕竟谁也不知道下一次访问的是哪个物理块,它和上个物理块不一定挨着的。

随机访问还有个变种就是相对访问,相对访问的意思是,将文件组织成一个个逻辑块,这个逻辑块是顺序的和物理无关的,读写数据时,通过这个逻辑块,然后将这个逻辑块映射到物理块,最终访问到数据,相对访问的好处就是文件的物理块如果重新组织了的话,那么对于用户来说是透明的,文件系统已经将逻辑块和物理块的映射关系调整了。

什么是目录?

目录分类

单层目录

文件多了就需要分门别类,这个符合人类的管理习惯,刚开始只有一层目录,这个目录叫做根目录,所有用户的文件都放在这个根目录下,如果文件系统文件不是那么的多,这个分类还是很简单高效的。

彻底搞懂文件系统

单层目录

两层目录

随着文 件越来越多,也是为了每个用户各自管理各自的文件,就发明了二层目录即在根目录下为每个用户创建了目录,这样每个用户的文件就变成了用户私有的了。

彻底搞懂文件系统

两层目录

树形目录

用户也要对他自己目录下的文件分门别类,这似乎是顺理成章的事情,于是乎树形目录诞生了,每个用户可以在他的目录下定义任意层次的子目录。

彻底搞懂文件系统

树形目录

无环有向图

无循环有向图的目的之一就是为了多个用户之间互相共享文件,共享的方式有很多,常用的方式就是文件链接,如下图所示

彻底搞懂文件系统

无环有向图

例如小黄用户的目录下有一个文件D,小王用户和小黄用户是合作者,他们共同对文件D进行访问,小王用户可以创建文件D的链接文件C,通过链接文件可以访问到文件D。

链接文件通常分为两类即软链接文件(符号链接)和硬链接文件,如上图的C和G文件。

软链接文件中存储的是原文件的路径(绝对路径或者相对路径),当访问软链接文件时,软链接文件根据文件路径一级级查找目录,找到原文件属性信息,然后才能根据原文件属性信息中的文件物理地址访问到原文件内容,硬链接存储的是原文件的文件属性信息的物理地址,因此硬链接不用查找目录,直接根据这个物理地址,就能找到原文件属性信息,然后再根据原文件属性信息中的物理地址访问原文件内容。

对于硬链接文件,它链家的原文件属性中会有个计数器,每次有一个硬链接文件指向原文件时,这个计数器就加1,当删除硬链接文件时,计数就减一,当计数器为0时,就可以删除原文件了,当然如果用户直接删除原文件,硬链接文件存储的物理地址会指向空,如果这个物理地址被重用的话,那么很可能硬链接文件会链接到一个新的文件,这个是很危险的,当然文件系统会解决这个问题。

对于软链接文件,它的删除对原文件没有任何影响,就好像原文件不知道有软链接文件的存在,当原文件被删除后,用户再次访问软链接文件时,才会发现原文件已经被删除了,此时可以提示用户原文件不存在了或者自动删除该软链接文件。

目录结构

每个目录是由目录条目构成,目录条目可以包括文件的所有属性,也可以只包括一部分文件属性,如下图所示为这两类目录条目的结构图

彻底搞懂文件系统

由上图所示,目录条目包括了文件的所有属性和文件物理地址,这个文件物理地址通常是文件的第一个物理块号,通过这个块号可以定位到文件的第一个物理块的内容,怎么定位文件的其它物理块是另外一个话题,后面单独讲。

彻底搞懂文件系统

由上图所示,目录条目包括了文件名称和文件索引块号,这个索引块号指向了一个物理块,这个物理块存储的是文件的所有属性和这个文件的内容的物理块号集合,通过物理块号集合可以定位到文件的全部或者部分物理块,怎么定位文件内容的其它物理块是另外一个话题,后面单独讲。

总体来讲,根目录通常是单独分配一块存储区域进行存储的,其它的目录和文件是另一块存储区域,很多操作系统例如unix,linux,目录是特殊的文件,文件被分为普通文件和目录,普通文件中存储的是文件内容,而目录存储的是目录条目,对于普通文件的目录条目,它可以定位到文件的数据,对于目录的目录条目,它定位的内容存储的是该目录下的目录条目。

文件打开表

当我们读取一个文件时,会先打开这个文件即调用open方法,然后根据open返回的一个整数即文件描述符来读取文件的内容,如下图所示

彻底搞懂文件系统

文件操作与文件打开表

调用打开文件时,会根据文件路径在辅助存储设备中找到目录,将目录下的目录条目缓存起来,然后根据文件名定位到目录条目,后续在访问这个目录条目时,就可以直接从缓存中读取,然后根据目录条目获取文件的属性信息,然后从文件的属性中过滤出与文件控制有关的属性即文件控制块即FCB,然后将FCB拷贝到内存中形成一个文件打开条目,将这个条目加入到系统文件打开表,文件打开条目不光包括FCB还包括一个计数器,这个计数器表示文件被进程打开的次数,每个进程打开这个文件时,这个计数器加1,当进程关闭文件时,这个计数器减1,当计数器为0时,系统文件打开列表会将这个文件打开条目删除掉。

另外,每个进程有单独的文件打开表,叫做进程的文件打开表,当进程打开文件时,会添加一个文件条目,这个文件条目里面存储的是当前文件的读写位置和一个指针,这个指针指向系统打开文件表中的文件打开条目,这样每个进程可以独立地维护文件的读写位置,另外可以通过指针定位到文件打开条目,通过文件打开条目的FCB可以找到该文件的所有物理块,然后根据读写位置定位到具体的物理块,然后读取这个物理块内容。

什么是文件系统?

文件系统层次

文件系统对上即应用程序提供文件操作接口,这些文件操作接口的参数通常是人类能够理解的文件路径,对下将文件操作接口获取到的接口参数信息转化成底层的IO的请求,交由IO控制去处理,下面为一个可能的层次图

彻底搞懂文件系统

IO控制是由设备驱动程序和中断处理程序构成,它通常属于操作系统内核,实现了存储设备如磁盘数据到内存,内存到磁盘数据的互相交换,设备驱动程序将上层提供的高层抽象指令转化为底层的硬件指令,然后通过设备的IO控制器来进行读写操作。

文件系统也分层,每一层次做各层次的事情,同时提供接口给上一层调用,由上图所示,文件系统分为逻辑文件系统,文件组织系统,基本文件系统3个层次。

基本文件系统:负责找到合适的设备驱动程序,然后发送高层命令和参数(如访问的物理块号)给设备驱动程序。

文件组织系统:将文件的逻辑块转化为物理块,每个文件被分成多个逻辑块,这个逻辑块是顺序的,文件对应的物理块不一定是顺序的,因此就需要根据不同的文件分配方式翻译成物理块,交由基本文件系统处理,另外这个系统也负责空闲物理块的管理。

逻辑文件系统:用于管理文件的元数据,例如目录缓冲,系统文件打开表,根据文件路径定位目录条目等。

文件系统采用分层,可以最大程度地重用代码,比如如果换了逻辑文件系统,文件组织系统和基本文件系统的代码一样可以用。

文件系统装在哪里?

如下图所示为一个可能的文件系统分区图

彻底搞懂文件系统

一个磁盘可以被分成多个分区,上图为4个分区,磁盘的第一个扇区存储的是MBR即主引导扇区,这个扇区的前一部分为引导代码,后面为分区表,分区表有4个表项,表项包括了分区的起始地址和结束地址,以及该分区是不是活动分区。

因此MBR会找到活动分区,活动分区的起始位置为引导块,该引导块可以引导加载操作系统,紧接着存储着超级块,超级块存储了整体的inode总数,每个inode的地址,空闲位图的地址等,根目录是单独存储的,其它的文件和目录存储在分区的最后位置。

文件分配

上文所述文件是由多个物理块构成,那么文件系统是怎么将物理块分配给文件的,而文件又是从哪里获取组成它的物理块呢?

这个就是文件系统的物理块分配方案,常用的分配方案有以下四种,每个系统会选择其中一种分配方案,不会出现一个文件系统同时有两种分配方案。

连续分配

连续分配方案将多个连续的物理块分配给文件,如下图所示

彻底搞懂文件系统

连续分配

如上图所示,总共4个文件,文件的物理块都是连续的,所有的文件都是按照物理块编号顺序分配的,因此这类分配方案,在目录条目中存储起始物理块号就可以了,然后可以根据文件的大小和物理块大小,算出总共需要多少块,然后从起始物理块号顺序分配即可。

连续分配有两个缺点,第一个是如果文件被删除,它所占用的空间被释放,会形成外部碎片,随着文件的不断创建到了存储空间用完了,如果有个大一点的文件需要分配的话,可能没有一个连续的空间足够容纳这个大文件,这个时候只能对整个存储设备进行碎片整理,这是一项非常慢的工程,另外一个是文件大小不容易动态扩展,在最初创建文件时就规划好文件大小,这样又可能造成空间浪费,如果未来增大文件时,不得不再寻找一个合适的连续物理空间,这样导致文件重新物理块,涉及到大量的复制工作。

链接分配

链链接分配解决了外部碎片和文件扩展的问题,它将文件的物理块分散地存储,每个物理块头部有一个指针指向下一个物理块号,因此在文件的目录条目中根据起始物理块号可以不断地通过下个指针访问到所有的物理块号,如下图所示

彻底搞懂文件系统

链接分配

链接分配也有两个缺点

第一个是每个物理块都被指针占用了一部分,这样实际存储的内容就不是一个完整的物理块,如果要存储一个完整的物理块内容就需要两个物理块,这样访问这个一个文件时,需要访问存储设备2次,另外也会加大内部碎片,试想如果一个物理块大小为1KB,文件大小为1KB,文件得需要两个物理块,第二个物理块的内容就浪费了很多。

第二个问题是如果要随机访问一个物理块,需要从头开始不断通过下一个物理块号指针查找,需要进行很多次的IO操作才能定位到物理块,因此链接分配适用于顺序访问。

FAT分配

FAT分配可以说是链接分配的升级版,如下图所示

彻底搞懂文件系统

FAT分配

文件的下个物理块的指针不再存储在物理块中,而是在内核空间中维护了一个文件分配表即FAT,文件系统有多少个物理块,这个文件分配表就有多少个表项,我们可以把文件分配表理解为一个大数组,数组的索引就是物理块号,这个索引对应的数组项的内容就是下一个物理块号,最后一个物理块它的内容为-1。

这样看来通过FAT分配,文件既可以很方便地访问某一个物理块,物理块中又不需要存储下一个物理块的指针,解决了链接分配的两个痛点。

虽然如此,FAT也有痛点,FAT适用于小容量的文件系统,因为FAT分配表在内核空间,如果文件系统容量太大,这个FAT就会占用很大的内存空间,如果把物理块的大小调大,内部碎片又会变多,所以这个需要权衡,FAT不适合大容量的文件系统。

索引分配

索引分配和链接分配,FAT分配一样没有外部碎片,它解决了FAT只适用于小容量文件系统的痛点,索引分配可谓是大小通吃。

如下图所示为索引分配图

彻底搞懂文件系统

索引分配

由上图得知,目录条目中有一个索引块号,通过索引块号,可以定位到索引物理块,这个物理块中不仅存储了文件的所有属性,而且还存储了文件的物理块号,对于小一点的文件,索性块中的所有物理块号加起来就可以完全定位整个文件,对于大的文件,索引块中的物理块数就不够了,因此它有很多的变种。

彻底搞懂文件系统

unix的inode索引块

以上为unix的索引分配变种,每个索引块号包括了8个数据块号,这8个数据块号可以直接定位到文件物理内容,如果小文件的大小没有超过这8个物理块总和,指针数据块号也就不需要了,当文件比较大,8个物理块无法容纳时,就需要更多的物理块,这些多出来物理块号存储在另一个物理块即指针数据块,指针数据块中存储了更多的物理块号和一个指针数据块号,如果物理块还不够,就继续递归下去,直到够了或者空间用完为止。

彻底搞懂文件系统

linux的inode索引块

linux包括了12个数据块,这12个数据块可以直接定位到文件内容,剩下的3个块号,分别存储了一级,二级,三级数据块号,通过这种方式可以指数级地增加物理块号,减少指针数据块的层次。

空闲块管理

空闲块管理用于管理一个文件系统还有哪些空闲物理块可以使用,当文件扩容时就需要更多的空闲物理块满足需要,当文件删除时,释放了文件空间,文件占用的物理块就释放为空闲物理块。

空闲物理块管理一般采用两种方式:

链表式管理

如下图所示

彻底搞懂文件系统

链表式管理

由上图所示,链表式管理用n个物理块来存储空闲块,假如一个物理块大小为1KB,每个空闲物理块号占用4个字节,那么一个物理块就可以存储255个空闲物理块号,最后一个物理块号,如上图所示红色部分是下一个物理块的块号,而下一个物理块号又存储了255个空闲物理块号,如此递归下去。

通常来说,存储设备单独提供了用于存储空闲物理块的空间,这个空间通常是连续的,因此文件系统初始化的时候只需要在内存中维护这个连续空间的起始物理块号如上图所示的第一个空闲块号,当文件系统创建第一个文件时,会计算出需要的物理块数,然后根据第一个空闲块号,将第一个空闲块的内容加载到内存成为空闲块列表,从中找出空闲块,然后分配给文件,随着文件不断地创建,空闲块列表上的空闲块被分配完了即空闲块列表空了,因此文件系统会根据下一个物理块号找出下一个存储空闲块号的物理块,加载到内存中加入到空闲块列表,如此可以递归下去,直到满足需求,当文件被删除后,文件所占用的物理块会重新加入到空闲块列表中,当空闲块列表满了后,会被强制写入到存储设备中。

位图式管理

如下图所示

彻底搞懂文件系统

位图式管理

每个空闲物理块占用1个位,1表示物理块使用中,0表示物理块是空闲的,很明显位图占用的存储空间较链表少,因此可以将一个物理块的位图加载到内存中,这个位图表示的物理块较链表更多,这样有可能文件系统只需要一个物理块的位图就够了,另外创建文件时,可以将连续的空闲物理块分配给文件,提高了文件的读写效率。

文件系统性能

提供文件系统性能的几种方式

第一:将频繁读取的物理块缓存到文件系统中,这样下一次读取该物理块时,就直接从缓存中获取,从而提高文件系统性能。

第二:如果大量的读取操作是连续的,可以在读取当前物理块的同时,可以提前将下一个物理块读取到缓存中,这样获取下一个块的时候就直接从缓存中获取了,用户毫不知情,当然如果突然下一个物理块是随机的了,这个提前的读取就是一种浪费,这个需要权衡,只要整体上连续的概率大些,就会整体提高文件系统性能。

第三:在文件创建时,可以根据空闲块管理尽量将连续的空闲块分配给文件,这样文件的操作就是顺序的,就会减少例如磁盘寻道的次数,从而提高文件系统性能。

现在很多的存储设备都采用了固态硬盘,固态硬盘没有寻道,磁臂运动这些很费时间的操作,但是固态硬盘有个问题就是每个物理块有写入次数的限制,这个是个缺点,文件系统采用哪类存储设备需要进行权衡。

虚拟文件系统

一个操作系统上支持的文件系统有很多种类型,每个文件系统有各自的目录结构,如果将这些文件系统整合成一个目录结构,这样应用程序访问时就不需要考虑实际的目录结构的不同,另外一个就是还有很多的文件系统提供的接口各式各样,虽然这些接口的目的一样,但是接口名,参数类型都有些不同。

因此一个标准的文件系统,应该提供标准的文件接口例如read,write,close等,不能因为文件系统类型的不同要求用户编写不同的程序,如下图所示为虚拟文件系统。

彻底搞懂文件系统

虚拟文件系统

虚拟文件系统其实就是一个代理,它根据标准的文件接口,将这些接口的请求转发到不同的文件系统类型,用户无需知道底层用的哪类文件系统类型,即使增加了新的文件系统类型,对用户也是无感知的。

虚拟文件系统通过函数列表进行访问,这个函数列表由各类文件系统提供,这样虚拟文件系统就根据不同的文件系统找到不同的函数列表,然后根据函数和标准接口的映射,从函数列表中找到标准接口对应的函数,然后调用这些函数。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多