1、文件系统结构
为了改善I/O效率,内存和磁盘之间的I/O转移是以块为单位的,而不是以字节为单位,每块分为一个或多个扇区,根据磁盘驱动器的不同,扇区从32-4096B不等,通常为512B。
为了提供对磁盘的高效和便捷的访问,操作系统通过文件系统来轻松存储、定位、提取数据。
文件系统本身通常由许多不同的层组成。从上到下依次是:应用程序->逻辑文件系统->文件组织系统->基本文件系统->I/O控制->设备
设计中每层利用较低层的功能来创建新的功能来对更高层来服务。
I/O控制为最底层,由设备驱动程序和中断处理程序组成,实现内存与磁盘之间的信息传输,实质就是通过一些命令来控制磁盘的指针来访问磁盘上的数据。
基本文件系统只需要向合适的设备驱动程序发送一般命令就可以对磁盘上的物理块进行读写,每个块由其数值磁盘地址来标识。
文件组织模块知道文件及其逻辑块和物理块,由于知道所使用的文件分配类型和文件位置,文件组织模块可以将逻辑块地址转换成基本文件系统所使用的物理块地址。
逻辑文件系统管理元数据,元数据包括文件西游的所有结构数据,而不包括实际数据,逻辑文件系统根据给定符号文件名来管理目录结构,并提供给文件组织模块所需要的信息。
采用分层的结构实现文件系统,能够最大限度的减小重复的代码。
2、文件系统实现
2.1、分区与安装
磁盘布局因为操作系统而异,一个磁盘可以分成多个分区,或者一个卷可以横跨多个磁盘上的数个分区。
“生”磁盘是指没有合适文件系统的磁盘空间。
引导信息能保存在各个分区,它由自己的格式,因为在引导系统时系统并没有文件系统设备驱动程序,所以并不能解释文件系统格式。
根分区包括操作系统内核和其他系统文件,在引导时装入内存,其他卷根据不同操作系统可以再引导时自动装入或在此后手动装入。
2.2、虚拟文件系统
绝大多数操作系统都是通过面向对象技术来简化、组织和模块化实现过程,使用这些方法允许不同文件系统类型可以通过同样的结构来实现,这也包括网络文件,采用数据结构和子程序,可以分开基本系统调用和实现细节,因此,文件系统,主要分为三个层次:从上到下依次是文件系统接口->VFS接口->本地文件系统。
第二层VFS为虚拟文件系统,主要由两个作用:
1)VFS层通过定义一个清晰的VFS接口,以将文件系统的通用接口操作和具体实现分开。多个VFS接口的实现可以共存在同一台机器,它允许访问已装在本地的多个类型的文件。
2)VFS提供了在网络上唯一标识一个文件的机制。
VFS能区分本地文件和远程文件,根据文件系统类型来进一步区分不同本地文件。
VFS根据文件系统类型调用特定文件类型操作以处理本地请求,通过调用NFS协议程序来处理远程请求。
3、目录实现
目录分配和目录管理算法的选择对文件系统的效率、性能和可靠性有很大的影响。
3.1、线性列表
最为简单的目录实现算法就是使用存储文件名和数据块指针的线性列表,这种方法编程简单但是运行时较为费时。
要创建新文件,必须首先搜索目录以确定没有同样名称的文件存在,接着,在目录后增加一个新条目,要删除文件时,根据给定文件名搜索目录,接着释放分配给他的空间,如果需要重用目录条目,可以有许多办法,可以将目录条目标记为不再使用或者可以将它加到空闲目录条目上。
3.2、哈希表
用于文件目录的另一个数据结构就是哈希表,采用这种方法时,处理使用线性列表存储目录条目外,还使用了哈希数据结构。哈希表根据文件名得到一个值,并返回一个指向线性列表中元素的指针,因此,它大大减小了目录搜索时间。
哈希表的最大困难就是其通常固定的大小和哈希函数对大小的依赖性。
4、分配方法
4.1、连续分配
连续分配方法要求每个文件在磁盘上占有一组连续的块。
一个文件的目录条目包括开始块的地址和该文件所分配的区域的长度。对于一个连续分撇文件的访问很容易,要顺序访问,文件系统会记住上次访问过块的磁盘地址,如需要可读入下一块,因此,连续分配支持顺序访问和直接访问。
连续分配也存在一些问题:很难为新文件找到空间。也就是从一个空闲孔列表中寻找一个满足大小为n的空间,从一组空闲孔中寻找一个空闲孔最为常用的策略是首次适用和最优适用,这两种策略在空间使用方面不相上下,但是首次适合运行更快。
连续分配的另外一个问题是确定一个文件需要多少空间,我们在保存之后很可能要增加或删除文件内容,那么保存在空间大小就很难确定。为了解决这个问题,有的操作系统使用修正的连续分配,该方案分配一块连续的空间,当空间不够时,另一块被称为扩展的连续空间会添加到原来的分配中,这样,文件块的位置就成为能够为开始地址、块数、加上一个指向下一个扩展的指针,如此,解决文件大小问题。
4.2、链接分配
链接分配解决了连续分配的所有问题,采用链接分配,每个文件时磁盘块的链表,磁盘块分布在磁盘的任何地方。
要创建一个新文件,可以简单的在目录中增加一个新条目,对于链接分配,每个目录条目都有一个指向文件首块的指针。要写文件就会通过空闲空间管理系统找到一个空闲块,然后这个新块就会被写入并链接到文件的尾部,要读文件,可以通过块到块的指针,简单的读块。
链接分配也存在问题,首先,链接分配不支持文件的直接访问。
链接分配的另一个缺点是指针需要空间,存在空间浪费,解决这个问题,我们需要尽可能的降低指针所占空间。所以我们常用的解决方法是将多个块组成簇,按照簇而不是按照块来分配。
链接分配的另一个问题是可靠性,假设我们文件中的一个指针丢失,那么该指针之后的内容也相应丢失,可靠性差。
一种链接分配方法的变种是文件分配表。目录条目包含文件首块的块号码,根据块号码索引的FAT条目包含文件下一块的块号码,这种链接会一直存在下去,直到最后一块。
4.3、索引分配
链接分配解决了连续分配的外部碎片和大小声明问题,但是,如果不使用FAT,那么链接分配还是不支持世界访问,因此,我们提出索引分配,索引分配通过把所有的指针放到一起,通过索引块解决问题。
每个文件都有其索引块,这是一个磁盘块地址的数组。
当创建文件时,索引块的所有指针都是空,当首次写入第i块时,先从空闲空间管理器中得到一块,再将其地址写到索引块的第i个条目。
索引分配支持直接访问,且没有外部碎片问题,但是索引分配会浪费空间,索引块指针的开销通常比链接分配指针的开销大。所以我们需要尽可能的降低索引块的大小,主要有下面几种方案:
链接方案:一个索引块通常为一个磁盘块,为了处理大文件,可以将多个索引块链接起来。
多层索引:链接表示的一种变种是用第一层索引块指向一组第二层索引块,第二层索引块再指向文件块。
组合方案:在UFS中使用的另一种方案是将索引块的十五个指针存在文件的inode中,,这其中前12个指针指向直接块,它们包含了能存储文件数据的块的地址,因此,小文件不需要其他的索引块,但是如果块大小为4KB,那么不超过48KB的文件可以直接访问,其他三个指针指向间接块,第一个间接块指针为一级间接块的地址,一级间接块为索引块,它包含的不是数据,而是那些包含数据块的地址。
5、空闲空间管理
为了记录空闲磁盘空间,系统需要维护一个空闲空间链表,空闲空间链表记录了所有空闲磁盘空间
5.1、位向量
空闲空间表实现为位图或者位向量,每块用一位表示,如果一块为空闲,那么其位为“1”,如果一块已分配,那么其位为“0”
这种方法的主要优点是查找磁盘上第一个空闲块和n个连续空闲块时相对简单和高效
5.2、链表
空闲空间的另一种方法是将所有空闲磁盘块用链表链接起来,并将其指向第一空闲块的指针保存在磁盘的特殊位置上,同时也缓存在内存中,不过这种方法的效率不高,要遍历整个表时,需要读入每一块,这就需要大量的I/O空间,好在遍历整个表并不是一个经常操作。
5.3、组
对空闲链表的一个改进是将n个空闲块的地址存在第一个空闲块中,这些块中的前n-1个确实为空,而最后一块包含另外n个空闲块的地址。
5.4、计数
记录第一块的地址和紧跟第一块的连续的空闲块的数量n,这样,空闲空间表的每个条目包括磁盘地址和数量。