文件系统实现
前言
文件系统提供了在线存储和访问包括数据和程序在内的文件内容的机制。文件系统永久地驻留在外存上,外存可以永久存储大量数据。
本章主要讨论在最常用的外存即磁盘上,如何存储和访问文件的有关问题。讨论用各种方法来组织文件,分配磁盘空间,恢复空闲空间,跟踪数据位置,以及其他操作系统部分给外存提供的接口以及性能问题。
一、文件系统结构
为什么选择磁盘作为外存空间来维持系统:
- 可以原地重写
- 可以直接访问磁盘上的任意一块信息。
- 经济实惠。
- 我们在下一章将会详细讨论磁盘结构。
内存与磁盘: 为了改善I/O效率,内存与磁盘直接的I/O转移是以块为单位而不是以字节为单位来进行的。每块为一个或多个扇区。根据磁盘驱动器的不同,扇区从32~4096B不等,通常为512B。
文件系统通常进行分层设计:
- 设备: 即I/O设备,包括打印机,键盘等等。
- I/O控制: 由设备驱动程序和中断处理程序组成,实现内存和磁盘之间的信息传输。
- 基本文件系统: 只需要向合适的设备驱动程序发送一般命令就可对磁盘上的物理块进行读写。每个块有其数值磁盘地址来标识。
- 文件组织模块: 知道文件及其物理块和逻辑块。由于知道所使用的文件分配类型和文件的位置,文件组织模块可以将逻辑块地址转换成基本文件系统所用的物理块地址。
- 逻辑文件系统: 逻辑文件系统管理元数据。元数据包括文件系统的所有数据结构,而不包括实际数据。逻辑文件系统根据给定符号文件名来管理目录结构,并提供给文件组织模块所需要的信息。逻辑文件系统通过文件控制块来维护文件结构。
- 文件控制块(file control block,FCB): 包含文件的信息包括拥有者、权限、文件内容的位置。
- 用户程序
二、文件系统实现
1. 概述
不同操作系统有不同的文件系统,但是它们还是有一些通用规律
通用规律部分1:
- (每个卷的)引导控制块(boot control block): ** 包括系统从该卷引导操作系统所需要的信心。如果磁盘没有操作系统,那么这块的内容为空。它通常为卷的第一块。UFS文件系统称之为引导块**,NTFS文件系统称之为分区引导扇区。
- (每个卷的)卷控制块(volume control block): ** 包括卷的详细信息,如分区的块数、块的大小、空闲块的数量和指针、空闲FCB的数量和指针等。UFS文件系统称之为超级块(superblock),NTFS文件系统称之为主控文件表**。
- 目录结构: 每个文件系统的目录结构用来组织文件。UFS中它包含文件名和相关的索引节点号。NTFS中它存储在主控文件表中。
内存内信息用于文件系统管理并通过缓存来提高性能。这些数据在文件系统安装的时候被加载,卸载的时候被丢弃。
通用规律部分2:
- 一个内存中的安装表,包括所有安装卷的信息。
- 一个内存中的目录结构缓存,用来保存近来访问过的目录信息。
- 系统范围内的打开文件表包括每个打开文件的FCB副本和其他信息。
- 单个进程的打开文件表包括一个指向系统范围内已打开文件表中合适条目的指针和其他信息。
典型的FCB组成:
FCB的使用: 为了创建一个新文件,应用程序调用逻辑文件系统。逻辑文件系统知道目录结构形式。为了创建一个新文件,它将分配一个新的FCB(如果文件系统实现在文件系统被创建的时候就已经创建了所有的FCB,那么只是从空闲的FCB集合中分配一个)。然后系统将目录信息读入内存,用新的文件名更新该目录和PCB,并将结果写回磁盘。
内存中的文件系统结构:左打开文件,右读文件
2. 分区与安装
**磁盘具有生(raw)熟(cooked)之分: **
- 生磁盘指没有文件系统的磁盘,因为没有适合的文件系统来存储特定数据,有的数据库使用生磁盘。
- 熟磁盘即含有文件系统的磁盘。
引导信息: 引导信息能保存在各个分区之中。它有自己的格式,因为在引导时系统并没有文件系统设备驱动程序。
根分区: 包括操作系统内核和其他系统文件,在引导时装入内存。
3. 虚拟文件系统(virtual file system ,VFS)
虚拟文件系统是物理文件系统与文件系统服务之间的一个接口层(VFS interface),它对每个物理文件系统的所欲细节进行抽象,并未这些不同的文件系统提供了一个统一的系统调用接口。
- VFS层通过定义一个清醒的VFS接口,以将文件系统的通用操作和具体实现分开。
- VFS提供了在网络上唯一标识一个文件的机制。
- VFS能区分本地文件和远程文件,根据文件类型可以进一步区分不同本地文件。
三、目录实现
1. 线性列表
最为简单的目录实现方法是使用存储文件名和数据块指针的线性列表。这种方法编程简单但运行较为费时。
2. 哈希表
哈希函数易于查找,但是会出现冲突,降低效率。但是仍要优于线性列表。
四、分配方法
这里讨论的是文件在磁盘上如何存储的问题。
1. 连续分配
连续分配方法要求每个文件在磁盘上占有一组独立的块。分配策略可结合第八章的内存分配策略(首次、最优、最大)。
优点: 实现简单,支持顺序访问和直接访问,访问速度快,效率高,适合文件内容不太进行变动的情况。
缺点: 因为文件被分配到连续空间,文件大小难以扩展;会产生外部碎片。
- 解决外部碎片问题: 转移到另一磁盘,将空闲空间进行合并,再转回去。但这样会存在停机时间。
- 解决不能扩展问题: 通过指针,当空间不够时,再扩展一块空间。原来的空间通过指针进行寻找扩展空间。
磁盘空间的连续分配:
2. 链接分配
链接分配解决了连续分配的所有问题。采用链接分配,每个文件是磁盘块的链表,磁盘块分布在磁盘的任何地方。
优点: 实现简单,只需要首地址;没有外部碎片问题,文件可以扩展,不需要提前声明文件大小。
缺点:
- 每个文件块都有指针,占用空间;
- 无法实现直接读取;
- 可靠性差,一个中间数据块的丢失都会导致链的断裂;
解决方法:
- 减少指针空间占用: 将多个块组成簇(cluster),并按簇而不是按块来分配(但是这样会增加内部碎片)。
- 增加可靠性: 采用文件分配表(File Allocation table, FAT)。每个卷的开始部分用于存储该FAT。每块都在FAT表中有一项,该表可以通过块号码来索引。FAT的使用与链表相似。目录条目含有文件首块的块号码。根据块号号码索引的FAT条目包含文件下一块的块号码。这条链会一直继续到最后一块,该块对应FAT条目的值为文件结束值。
磁盘空间的连接分配:
FAT:
FAT优点: 通过I/O一次性读入FAT表进入内存,就可以实现对文件任意位置的随机访问;稳定性高。
FAT缺点: 如果不采用缓存将FAT表读入内存,会导致每次访问都要先访问FAT,导致访问时间长。
3. 索引分配
链接分配解决了连续分配的外部碎片和大小声明问题。但是,如果不用FAT,那么链接分配就不能有效支持直接访问,这是因为指针与块一起分布在整个磁盘,且必须按顺序读取。
索引分配: 通过把所有指针放在一起,即通过索引块 解决了这个问题。
优点: 支持顺序访问,随机访问;文件可扩展,文件大小无需提前声明;稳定性高;没有外部碎片。
缺点: 浪费空间。比如,每个文件只有一块或两块长。采用链接分配,每块只浪费一个指针。采用索引分配,尽管只有一个或两个指针为非空,也必须分配一个完整的索引块。
问题解决:
- 为了处理大文件,可以将多个索引块链接起来,也可以使用多层索引。
- 为了不让小文件浪费空间,可以采用组合方案。比如UNX文件系统中的inode。
UNX中的inode:
五、空闲空间管理
1. 位向量(bit vector)
位向量: 将空闲空间表现为位图,每块空闲空间用1位表示,如果空闲则为1,已分配为0。比如10101101010100110110101010110。
优点: 实现相对简单;容易产生连续空间分配的文件。
缺点: 如果磁盘块数过多,对应的位图也会很大。
2. 链表
链表: 将所有空闲磁盘块用链表连接起来,并将指向第一空闲块的指针保存在磁盘的特殊位置,同时也缓存在缓存中。
优点: 查询时占用内存空间小(读一块大部分就够用了)
缺点: 可靠性差,不易查找大量空闲块。且查找费时。
3. 组
对于空闲链表而言,可以将n个空闲块的地址存在第一个空闲块中,之后的n-1块确实为空,最后一块包含另外n个空闲块的地址。
优点: 可以快速找到大量空闲块的地址。
4. 计数
通常,有多个连续块需要同时分配或释放,尤其是在使用连续分配和采用簇时。因此,可以记录第一块的地址和紧跟第一块的连续的空闲块的数量n。
这样,空闲空间表的每个条目包括磁盘地址和数量。虽然每个条目会比原来需要更多空间,但是表的总长度会更短,这是因为连续块的数量常常大于1。