一、文件系统结构
文件系统(File system)提供高效和便捷的磁盘访问,以便允许存储、定位、提取数据。文件系统有两个不同的设计问题:第一个问题是,定义文件系统的用户接口,它涉及定义文件及其属性、所允许的文件操作、如何组织文件的目录结构。第二个问题是,创建算法和数据结构,以便映射逻辑文件系统到物理外存设备。现代操作系统有多种文件系统类型,因此文件系统的层次结构也不尽相同。下图是一个合理的文件系统层次结构。
1. I/O 控制
包括设备驱动程序和中断处理程序,在内存和磁盘系统之间传输信息。设备驱动程序将输入的命令翻译成底层硬件的特定指令,硬件控制器利用这些指令使 I/O 设备与系统交互。设备驱动程序告诉 I/O 控制器对设备的什么位置采取什么动作。
2. 基本文件系统
向对应的设备驱动程序发送通用命令,以读取和写入磁盘的物理块。每个物理块由磁盘地址标识。该层也管理内存缓冲区,并保存各种文件系统、目录和数据块的缓存。在进行磁盘块传输前,分配合适的缓冲区,并对缓冲区进行管理。管理它们对于系统性能的优化至关重要。
3. 文件组织模块
组织文件及其逻辑块和物理块。文件组织模块可以将逻辑块地址转换成物理块地址,每个文件的逻辑块从 0 到 N 编号,它与数据的物理块不匹配,因此需要通过转换来定位。文件组织模块还包括空闲空间管理器,以跟踪未分配的块,根据需求提供给文件组织模块。
4. 逻辑文件系统
用于管理元数据信息。元数据包括文件系统的所有结构,而不包括实际数据(或文件内容)。逻辑文件系统管理目录结构,以便根据给定文件名称为文件组织模块提供所需要的信息。它通过文件控制块来维护文件结构。逻辑文件系统还负责文件保护。
5. 其他分层方式(了解)
用一个例子来辅助记忆文件系统的层次结构:
假设某用户请求删除文件 “D:/工作目录/学生信息.xlsx” 的最后 100 条记录。
- 用户需要通过操作系统提供的接口发出上述请求——用户接口
- 由于用户提供的是文件的存放路径,因此需要操作系统一层一层地查找目录,找到对应的目录项——文件目录系统
- 不同的用户对文件有不同的操作权限,因此为了保证安全,需要检查用户是否有访问权限——存取控制模块(存取控制验证层)
- 验证了用户的访问权限之后,需要把用户提供的 “记录号” 转变为对应的逻辑地址——逻辑文件系统与文件信息缓冲区
- 知道了目标记录对应的逻辑地址后,还需要转换成实际的物理地址——物理文件系统
- 要删除这条记录,必定要对磁盘设备发出请求——设备管理程序模块
- 删除这些记录后,会有一些盘块空闲,因此要将这些空闲盘块回收——辅助分配模块
二、文件系统布局
1. 文件系统在磁盘中的结构
文件系统存放在磁盘上,多数磁盘划分为一个或多个分区,每个分区中有一个独立的文件系统。文件系统可能包括如下信息:启动存储在那里的操作系统的方式、总的块数、空闲块的数量和位置、目录结构以及各个具体文件等。下图为一个可能的文件系统布局。
简单描述如下:
1)主引导记录(Master Boot Record, MBR)
位于磁盘的 0 号扇区,用来引导计算机,MBR 后面是分区表,该表给出每个分区的起始和结束地址。表中的一个分区被标记为活动分区,当计算机启动时,BIOS 读入并执行 MBR 。MBR 做的第一件事是确定活动分区,读入它的第一块,即引导块。
2)引导块(boot block)
MBR 执行引导块中的程序后,该程序负责启动该分区中的操作系统。为统一起见,每个分区都从一个引导块开始,即使它不含有一个可启动的操作系统,也不排除以后会在该分区安装一个操作系统。Windows 系统称之为分区引导扇区。除了从引导块开始,磁盘分区的布局是随着文件系统的不同而变化的。
3)超级块(super block)
包含文件系统的所有关键信息,在计算机启动时,或者在该文件系统首次使用时,超级块会被读入内存。超级块中的典型信息包括分区的块的数量、块的大小、空闲块的数量和指针、空闲的 FCB 数量和 FCB 指针等。
4)文件系统中空闲块的信息
可以使用位示图或指针链接的形式给出。后面也许跟的是一组 i 结点,每个文件对应一个结点,i 结点说明了文件的方方面面。接着可能是根目录,它存放文件系统目录树的根部。最后,磁盘的其他部分存放了其他所有的目录和文件。
2. 文件系统在内存中的结构
内存中的信息用于管理文件系统并通过缓存来提高性能。这些数据在安装文件系统时被加载,在文件系统操作期间被更新,在卸载时被丢弃。这些结构的类型可能包括:
- 内存中的安装表(mount table),包含每个已安装文件系统分区的有关信息。
- 内存中的目录结构的缓存包含最近访问目录的信息。对安装分区的目录,它可以包括一个指向分区表的指针。
- 整个系统的打开文件表,包含每个打开文件的 FCB 副本及其他信息。
- 每个进程的打开文件表,包含一个指向整个系统的打开文件表中的适当条目的指针,以及其他信息。
为了创建新的文件,应用程序调用逻辑文件系统。逻辑文件系统知道目录结构的格式,它将为文件分配一个新的 FCB 。然后,系统将相应的目录读入内存,使用新的文件名和 FCB 进行更新,并将它写回磁盘。
一旦文件被创建,它就能用于 I/O 。不过,首先要打开文件。系统调用 open() 将文件名传递给逻辑文件系统。调用 open() 首先搜索整个系统的打开文件表,以确定这个文件是否已被其他进程使用。如果已被使用,则在单个进程的打开文件表中创建一个条目,让其指向现有整个系统的打开文件表的相应条目。该算法在文件已打开时,能节省大量开销。如果这个文件尚未打开,则根据给定文件名来搜索目录结构。部分目录结构通常缓存在内存中,以加快目录操作。找到文件后,它的 FCB 会复制到整个系统的打开文件表中。该表不但存储 FCB ,而且跟踪打开该文件的进程的数量。然后,在单个进程的打开文件表中创建一个条目,并且通过指针将整个系统打开文件表的条目与其他域(如文件当前位置的指针和文件访问模式等)相连。调用 open() 返回的是一个指向单个进程的打开文件表中的适当条目的指针。以后,所有文件操作都通过该指针执行。一旦文件被打开,内核就不再使用文件名来访问文件,而使用文件描述符(Windows 称之为文件句柄)。
当进程关闭一个文件时,就会删除单个进程打开文件表中的相应条目,整个系统的打开文件表的文件打开数量也会递减。当所有打开某个文件的用户都关闭该文件后,任何更新的元数据将复制到磁盘的目录结构中,并且整个系统的打开文件表的对应条目也会被删除。
三、虚拟文件系统
普通的文件系统:
虚拟文件系统(VFS)为用户程序提供了文件系统操作的统一接口,屏蔽了不同文件系统的差异和操作细节,如下图所示。用户程序可以通过 VFS 提供的统一调用函数(如 open() 等)来操作不同文件系统(如 ext3 等)的文件,而无须考虑具体的文件系统和实际的存储介质。
虚拟文件系统的特点:
① 向上层用户进程提供统一标准的系统调用接口,屏蔽底层具体文件系统的实现差异。
② VFS 要求下层的文件系统必须实现某些规定的函数功能,如:open / read / write 。一个新的文件系统想要在某操作系统上被使用,就必须满足该操作系统 VFS 的要求。
③ 每打开一个文件,VFS 就在主存中新建一个 vnode ,用统一的数据结构表示文件,无论该文件存储在哪个文件系统。
注意:vnode 只存在于主存中,而 inode 既会被调入主存,也会在外存中存储。
虚拟文件系统采用了面向对象的思想,它抽象出一个通用的文件系统模型,定义了通用文件系统都支持的接口。新的文件系统只要支持并实现这些接口,即可安装和使用。以 Linux 中调用 write() 操作为例,它在 VFS 中通过 sys_write() 函数处理,sys_write() 找到具体文件系统,将控制权交给该文件系统,最后由具体文件系统与物理介质交互并写入数据,如下图所示。
为了实现 VFS ,Linux 主要抽象了四种对象类型。每个 VFS 对象都存放在一个适当的数据结构中,其中包括对象的属性和指向对象方法(函数)表的指针。
- 超级块对象:表示一个已安装(或称挂载)的特定文件系统。
- 索引结点对象:表示一个特定的文件。
- 目录项对象:表示一个特定的目录项。
- 文件对象:表示一个与进程相关的已打开文件。
Linux 将目录当作文件对象来处理,文件操作能同时应用于文件或目录。文件系统是由层次目录组成的,一个目录项可能包含文件名和其他目录名。目录项作为单独抽象的对象,是因为目录可以层层嵌套,以便于形成文件路径,而路径中的每一部分其实就是目录项。
1. 超级块对象
超级块对象对应于磁盘上特定扇区的文件系统超级块,用于存储已安装文件系统的元信息,元信息中包含文件系统的基本属性信息,如文件系统类型、文件系统基本块的大小、文件系统所挂载的设备、操作方法(函数)指针等。其中操作方法(函数)指针指向该超级块的操作方法表,包含一系列可在超级块对象上调用的操作函数,主要有分配 inode 、销毁 inode 、读 inode 、写 inode 、文件同步等。
2. 索引结点对象
文件系统处理文件所需要的所有信息,都放在一个称为索引结点的数据结构中,索引结点对文件是唯一的。只有当文件被访问时,才在内存中创建索引结点对象,每个索引结点对象都会复制磁盘索引结点包含的一些数据。该对象中有一个状态字段表示是否被修改,其值为 “脏” 时,说明对应的磁盘索引结点必须被更新。索引结点对象还提供许多操作接口,如创建新索引结点、创建硬链接、创建新目录等。
3. 目录项对象
由于 VFS 经常执行切换到某个目录这种操作,为了提高效率,便引入了目录项的概念。目录项对象是一个路径的组成部分,它要么是目录名,要么是文件名。例如,在查找路径名 /test 时,内核为根目录 “/” 创建一个目录项对象,为根目录下的 test 创建一个第二级目录项对象。目录项对象包含指向关联索引结点的指针,还包含指向父目录和指向子目录的指针。不同于前面两个对象, 目录项对象在磁盘上没有对应的数据结构,而是 VFS 在遍历路径的过程中,将它们逐个解析成目录项对象的。
4. 文件对象
文件对象代表进程打开的一个文件。可以通过 open() 调用打开一个文件,通过 close() 调用关闭一个文件。文件对象和物理文件的关系类似于进程和程序的关系。由于多个进程可以打开和操作同一文件,所以同一文件在内存中可能存在多个对应的文件对象,但对应的索引结点和目录项是唯一的。文件对象仅在进程观点上代表已经打开的文件,它反过来指向其索引结点。文件对象包含与该文件相关联的目录项对象,包含该文件的文件系统、文件指针等,还包含在该文件对象上调用的一系列操作函数。
下图所示是一个进程与文件进行交互的简单实例。三个不同的进程已打开了同一个文件,其中两个进程使用同一个硬链接。在这种情况下,每个进程都使用自己的文件对象,但只需要两个目录项对象,每个硬链接对应一个目录项对象。这两个目录项对象指向同一个索引结点对象,这个索引结点对象标识的是超级块对象及随后的普通磁盘文件。
VFS 还有另一个重要作用,即提高系统性能。最近最常使用的目录项对象被放在目录项高速缓存的磁盘缓存中,以加速从文件路径名到最后一个路径分量的索引结点的转换过程。
对用户来说,不需要关心不同文件系统的具体实现细节,只需要对一个虚拟的文件操作界面进行操作。VFS 对每个文件系统的所有细节进行抽象,使得不同的文件系统在系统中运行的其他进程看来都是相同的。严格来说,VFS 并不是一种实际的文件系统,它只存在于内存中,不存在于任何外存空间中。VFS 在系统启动时建立,在系统关闭时消亡。
四、文件系统挂载
如文件在使用前要打开那样,文件系统在进程使用之前必须先安装,也称挂载(Mounting)。将设备中的文件系统挂载到某个目录后,就可通过这个目录来访问设备上的文件。注意,这里的设备指的是逻辑上的设备,如一个磁盘上的不同分区都可视为不同的设备。
文件系统挂载(mounting),即文件系统安装 / 装载——如何将一个文件系统挂载到操作系统中?
Windows 系统维护一个扩展的两级目录结构,用驱动器字母表示设备和卷。卷具有常规树结构的目录,与驱动器号相关联,还含有指向已安装文件系统的指针。特定文件的路径形式为 driver-letter:\path\to\fle ,访问时,操作系统找到相应文件系统的指针,并遍历该设备的目录结构,以查找指定的文件。新版的 Windows 允许文件系统安装在目录树下的任意位置,就像 UNIX 一样。在启动时,Windows 操作系统自动发现所有设备,并且安装所有找到的文件系统。
UNIX 使用系统的根文件系统,它是在系统启动时直接安装的,也是内核映像所在的文件系统。除了根文件系统,所有其他文件系统都要先挂载到根文件系统中的某个目录后才能访问。其他文件系统要么在系统初始化时自动安装,要么由用户挂载在已安装文件系统的目录下。安装文件系统的这个目录称为安装点,同一个设备可以有多个安装点,同一个安装点同时只能挂载一个设备。将设备挂载到安装点之后,通过该目录就可以读取该设备中的数据。
假定将存放在磁盘 /dev/fd0 上的 ext2 文件系统通过 mount 命令安装到 /flp:
mount -t ext2 /dev/fd0 /flp
如需卸载该文件系统,可以使用 umount 命令。
五、小结
1、什么是文件系统?
操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:与文件管理有关的软件、被管理文件及实施文件管理所需的数据结构。
2、文件系统要完成哪些功能?
对于用户而言,文件系统最主要的功能是实现对文件的基本操作,让用户可以按名存储和查找文件,组织成合适的结构,并应当具有基本的文件共享和文件保护功能。对于操作系统本身而言,文件系统还需要管理与磁盘的信息交换,完成文件逻辑结构和物理结构上的变换,组织文件在磁盘上的存放,采取好的文件排放顺序和磁盘调度方法以提升整个系统的性能。