Linux 版 (精华区)
发信人: netiscpu (网中自由鸟), 信区: Linux
标 题: Linux实用教程(部分)(6)
发信站: 哈工大紫丁香 (Thu May 20 18:08:05 1999), 转信
第十三章
文 件 系 统
Linux 操作系统的一个重要特点是它支持许多不同类型的文件系统。Linux 中最普遍使
用的文件系统是 Ext2,但 Linux 也能够支持 FAT、VFAT、FAT32、MINIX 等不同类型
的文件系统。
磁盘在经过分区之后,单个的物理磁盘就被划分为多个逻辑分区,每个分区上可存在一
个文件系统。我们知道,块设备实际是可以包含文件系统的设备,不管块设备的具体构
造如何,Linux 文件系统均将它们当作线性块的集合而访问。每个块设备驱动程序的任
务,就是将逻辑块的请求转化为对应块设备可以理解的命令。类似地,对文件系统来说
,不管基础的块设备是什么,它都能够进行一致的操作。利用 Linux 的文件系统,用
户根本不用关心由不同硬件控制器控制的,处于不同物理介质上的不同文件系统之间的
区别。如果将网络服务器的磁盘或分区挂装到本地文件系统上,用户可以和操作本地文
件系统一样操作它们。
文件系统中的文件实际是数据的集合。文件系统不仅包含了这些文件中的数据,而且还
包含文件系统的结构、符号链接以及文件的安全保护等信息。
MINIX 是 Linux 最初使用的文件系统,但是由于 MINIX 文件系统实际是教学用操作系
统 MINIX 的文件系统,因此其功能尚不完备,并且性能欠佳。因此,1992 年 4 月份
,专为 Linux 的设计的第一个文件系统 EXT(Extended File System)应运而生,但
是,EXT 文件系统仍然有些粗糙,而且性能也同样是个问题。1993 年,在许多人的努
力下,EXT 2 (Second Extended File System)文件系统作为 EXT 文件系统的兄弟诞
生了。EXT 2 完全具备了高级文件系统的特点,性能也非常好。
在 EXT 文件系统的开发过程中,引入了一个非常重要的概念,即虚拟文件系统 (VFS
)。VFS 作为实际文件系统(EXT)和操作系统之间的接口,将实际的文件系统和操作
系统隔离开来。在 VFS 的帮助下,Linux 可以支持除 EXT 之外的许多文件系统类型。
各文件系统为 VFS 提供一致的接口,从而将不同文件系统的细节隐藏了起来。对操作
系统的其他部分,以及运行在操作系统之上的程序而言,所有的文件系统都是一样的。
对 VFS 来说,它一方面要保证快速访问实际文件系统中的数据,一方面还要保证文件
和数据能够正确保存。这两个方面实际是互相矛盾的,Linux 通过高速缓存协调这两个
需求。在高速缓存中,Linux 不仅缓存数据,而且还管理着操作系统和块设备之间的接
口。
本章主要讲述 Linux 的 Ext2 文件系统、虚拟文件系统以及缓冲区高速缓存。
13.1 Ext2 文件系统
Ext2 作为 Linux 可扩展的、功能强大的文件系统而设计,其发明者为 Remy Card。在
Linux 中,Ext2 是迄今为止最为成功的文件系统,而且作为所有 Linux 发行版本的
基本文件系统。
和其他文件系统一样,Ext2 文件系统中的文件保存在数据块中。对同一个 Ext2 文件
系统而言,其上所有的数据块大小都是一样的。和某些文件系统不同的是,不同的 Ex
t2 文件系统的数据块大小可以不一样,数据块大小实际在建立 Ext2 文件系统时指定
,并且作为文件系统的基本参数保存在文件系统中。单个文件所占用的空间按数据块为
单位分配,因此,如果某个文件的大小为 1025 字节,而数据块的大小为 1024 字节,
则该文件将占用两个数据块。平均而言,每个文件会浪费半个数据块的空间。但是,鱼
和熊掌不可兼得,我们为了提高文件系统的访问速度,为了文件系统便于管理,而不得
不浪费一些磁盘空间。
块设备上的大多数数据块用来保存文件的实际数据,而部分数据块则用来定义文件系统
的结构。Ext2 文件系统利用索引节点(inode)来描述文件系统的拓扑结构。在单个文
件系统中,每个文件对应一个索引节点,而每个索引节点有一个唯一的整数标识符。文
件系统中所有文件的索引节点保存在索引节点表中。Ext2 文件系统中的目录实际是一
种特殊文件,它们也有对应的索引节点,索引节点指向的数据块中包含该目录中所有的
目录项(文件、目录、符号链接等),每个目录项对应自己的索引节点。
图 13-1 Ext2 文件系统的物理布局
如图 13-1 所示,EXT 2 文件系统分布在块结构的设备上。文件系统不必了解数据块的
物理存储位置,它保存的是逻辑块的编号。实际上,由块设备驱动程序完成逻辑块编号
到物理存储位置的转换。Ext2 文件系统将逻辑分区划分为块组(Block Group)。每个
块组重复保存着对文件系统的完整性非常关键的信息,而同时也用来保存实际的文件和
目录数据。文件系统关键信息的重复存储有助于文件系统在发生故障时还原。
13.1.1 Ext2 索引节点
在 Ext2 文件系统中,索引节点是基本的数据块,每个文件和目录有且只有一个索引节
点与之对应。每个块组中的索引节点保存在索引节点表中,同时也保存有跟踪节点分配
情况的位图。图 13-2 是 Ext2 索引节点的结构图,其中的各个数据域在表 13-1 中描
述。
图 13-2 Ext2 索引节点
表 13-1 Ext2 索引节点数据域的描述
模式
该数据域包含索引节点所描述的对象类型,以及用户的许可信息。在 Ext2 中,每个
节点可描述一个文件、目录、符号链接、块设备、字符设备或一个 FIFO。
所有者信息
文件或目录所有者的用户和组标识符。
大小
文件以字节为单位的大小。
时间戳
该索引节点的创建时间以及索引节点的最后改动时间。
数据块
包含该索引节点所描述的数据的数据块指针。对于前 12 个数据块指针来说,它们指
向的数据块是包含实际文件数据的数据块,而后面的三个指针则包含间接数据块指针。
这里需要对上表中的数据块信息作进一步解释。前 12 个数据块指针直接指向包含文件
数据的数据块,而其后的三个数据块则是间接指针。这样的安排是为了适应文件的大小
变化。假如文件系统可保存的最大文件为4MB,而每个数据块的大小为 1024 字节。如
果索引节点全部利用直接数据块指针的话,则需要 4096 个数据块指针,而实际上,文
件系统中大部分的文件不可能有如此之大。如果要保存 1 MB 的文件,实际只需 1024
个数据块指针,因此大量的数据块指针被浪费了。
为了解决上述问题,多数利用索引节点的文件系统利用多级数据块指针。参见图 13-2
,假设数据块大小为 1024 字节。利用前 12 个直接指针,可保存最大为 12 KB 的文
件,对这些文件的访问将非常迅速。如果文件大小超过了 12 KB,则利用单级间接指针
,这一指针指向的数据块保存有一组数据块指针,这些指针依次指向包含实际数据的数
据块。如果每个数据块指针占用 4 个字节,则每个单级间接指针数据块可包含 1024
/ 4 = 256 个数据块指针,因此,利用直接指针和单级间接指针可保存 12 ′ 1024 +
256 ′ 1024 = 268 KB 的文件。当文件超过 268 K 字节时,再利用二级间接指针。
类似地,可计算利用直接指针、单级间接指针和二级间接指针时可保存的最大文件为:
12 ′ 1024 + 256 ′ 1024 + 256 ′ 256 ′ 1024 = 65804 KB
约为 65 MB 大小。依次类推,得出利用三级间接指针时,可保存的最大文件为168427
64 KB ? 16 GB。
13.1.2 Ext2 文件系统的超块
文件系统的超块(Superblock)包含了文件系统的基本大小和形式。其中包含的数据由
文件系统管理程序用来进行文件系统的维护。每个块组中包含有相同的超块信息,但通
常只需读取块组 0 的超块。超块中包含的信息在表 13-2 中列出。
表 13-2 Ext2 超块中的信息
幻数
幻数用来标识超块的类型。文件系统的挂装软件可据此判断是否为有效的 Ext2 文件
系统超块。当前的 Ext2 文件系统幻数为 0xEF53。
修订级别
包含两个数据,分别为主修订级别和次修订级别。文件系统的挂装软件可据此判断文
件系统是否支持某种特定的功能。实际上,该信息作为兼容性标志而存在。
挂装计数和最大挂装计数
利用这两个信息,系统可以判断文件系统是否需要彻底检验。文件系统每挂装一次,
挂装计数加 1。如果挂装计数达到最大挂装计数,则系统会提示应进行检查。
组块编号
保存该超块的组块编号。
数据块大小
文件系统的数据块大小,以字节为单位。
每组的数据块个数
每组的数据块个数,该值在建立文件系统时设置并保持不变。
空闲数据块
文件系统中空闲数据块的个数。
空闲索引节点
文件系统中空闲索引节点的个数。
首索引节点
文件系统第一个索引节点的编号。一般而言,Ext2 文件系统的首索引节点就是“/”
目录的目录项。
13.1.3 Ext2 块组描述符
每个块组中包含有一个数据结构描述该块组。和超块类似,所有块组的块组描述符重复
出现在所有的块组中。每个块组描述符包含的信息如表 13-3 所示。
表 13-3 块组描述符
数据块位图
包含块组数据块分配位图的数据块编号。
索引节点位图
包含块组索引节点分配位图的数据块编号。
索引节点表
包含块组索引节点表的起始数据块编号。
空闲块计数,空闲索引节点计数,已用目录计数
分配位图在分配或释放数据块、索引节点时使用。
块组描述符保存在每个块组超块的后面,每个块组中包含所有块组的描述符。块组 0
中包含的超块信息和块组描述符是实际使用的数据,由于文件系统被破坏而需要还原时
使用其他块组中重复的超块和块组描述符。
13.1.4 Ext2 目录
图 13-3 目录项的布局
如图 13-3 所示是内存中的目录项布局。在 Ext2 文件系统中,目录实际是特殊文件。
目录文件中包含了目录项的列表,而每个目录项包含的信息如表 13-4 所示。
表 13-4 目录项信息
索引节点
目录项的索引节点。
名称长度
目录项名称的长度,以字节为单位。
名称
目录项名称。
每个目录的前两个目录项始终是标准的“.”和“..”,分别代表“本目录”和“父目
录”。
当用户需要打开某个文件时,首先要指定文件的路径和名称,根据路径和名称,文件系
统可搜索到文件的对应索引节点,从而可读取文件中的数据。例如,假定要搜索文件
/home/WeiYM/mbox,在 Ext2 中,该文件的定位过程如下(参照图 13-4):
1. 文件系统按照超块中根目录的索引节点,以及索引节点中的数据块编号信息,可找
到根目录的目录项列表。
2. 在目录项列表中搜索名称为 home 的目录项,得到对应索引节点编号为 6。
3. 按照索引节点 6 中的数据块编号,可找到 /home 目录的目录项。
4. 在 /home 目录项列表中找出目录项 WeiYM,得到对应索引节点编号为 26。
5. 按照索引节点 26 中的数据块编号,可找到 /home/WeiYM 目录的目录项。
6. 从 /home/WeiYM 目录的目录项列表中可以获得文件 mbox 的索引节点。
获得目标文件的索引节点之后,随后就可以利用该索引节点访问文件中的数据。
13.1.5 Ext2 文件系统中数据块的分配和释放
我们知道,在频繁的文件创建和删除过程中,极容易使文件系统碎片化。过分碎片化的
文件系统中,逻辑上连续的数据块在物理上处于不连续的位置,从而导致文件的存取速
度减慢,最终影响系统性能。Ext2 文件系统通过为文件分配物理上相近的数据块,或
者至少让数据块保持在同一块组而避免文件系统的过分碎片化。只有在不得已时,Ext
2 文件系统才为单个文件在不同的块组中分配数据块。
图 13-4 文件的定位过程
当某个进程试图在文件中写入数据时,Linux 文件系统首先检查要写入的数据是否已超
过文件的末尾,如果已超过,则文件系统要分配新的数据块。在文件系统为该文件分配
数据块并将数据写入数据块之前,进程处于等待状态,完成实际的数据写入之后,进程
才能继续运行。因为数据块的分配或释放最终要修改超块中的信息,因此,进程在分配
数据块之前,首先要锁定超块,这样,其他要写数据的进程也必须等待当前写数据的进
程结束写过程。对超块的访问依照“先到先服务”的排队原则。
锁定超块之后,进程首先检查是否有足够的空闲数据块,如果没有足够的空闲块,则数
据的写入就要失败,进程最终会放弃对文件系统超块的锁定控制。
如果文件系统中有足够的空闲空间,则系统尝试分配新的数据块。如果 Ext2 文件系统
具备数据块的预分配功能,则只需从预分配块中获取一个数据块。预分配块实际是在已
分配数据块位图中预先保留的数据块。代表要分配数据块的文件的 VFS 索引节点中包
含两个专用于 Ext2 文件系统的数据域:prealloc_block 和 prealloc_count。这两个
数据域分别代表第一个预分配数据块的编号和预分配数据块的数目。通过指定文件的预
分配信息,可加速文件的数据块分配,并可保证在一定的文件大小范围内,保持文件数
据块的连续性。如果没有预分配的数据块,或者预分配功能被关闭,则 Ext2 文件系统
必须分配新的数据块。Ext2 文件系统首先查看和文件最后一个数据块相邻的数据块是
否空闲。逻辑上讲,这个数据块是最理想的,因为连续的数据块可保证最高的存取效率
。如果相邻数据块已被占用,则搜索范围加宽到和理想块相邻的 64 个块范围之内。如
果能够在相邻连续的 64 个块中找到空闲数据块,虽然该数据块并不是最理想的,但至
少比较接近,且处于同一块组中。
如果和理想块相邻连续的 64 个块中没有空闲块,则文件系统在其他块组中寻找空闲块
。块分配代码首先以 8 个块为单位(1 簇)分配空闲块,如果没有空闲簇,则寻找更
小的簇。上述过程持续进行,直到分配到数据块为止。如果数据块的预分配被打开,则
还需要更新prealloc_block 和 prealloc_count 数据域。
不管在哪里分配到了数据块,块分配代码需要更新新块所在块组的块位图,并在缓冲区
缓存中分配一个数据缓冲区。数据缓冲区由文件系统的支持设备标识符和块编号唯一标
识。缓冲区中的数据被清零,而缓冲区被标志为“脏”,表明其中的数据尚未写入物理
磁盘。最后,超块本身也被标志为“脏”,表明已被改动,最后解锁超块。
如果其他进程正在等待该超块解锁,则等待队列中的第一个进程可开始运行,并获得对
超块的互斥访问。进程数据写入新的数据块中,如果尚有更多的数据写入,则整个分配
过程再次开始。
数据块的释放过程相对直接,但仍然需要获得对超块的互斥访问。
13.2 虚拟文件系统
图 13-5 虚拟文件系统和实际文件系统之间的关系示意图
图 13-5 是虚拟文件系统和实际文件之间的关系示意图。虚拟文件系统必须管理所有可
在任意时刻挂装的不同文件系统。为此,Linux 虚拟文件系统维护一些描述整个虚拟文
件系统,以及实际已挂装文件系统的数据结构。虚拟文件系统对文件的描述方法和 Ex
t2 文件系统类似,也使用了超块和索引节点。为此,有必要描述虚拟文件系统的超块
和索引节点,以便能够正确区分 VFS 和 Ext2。
每个文件系统在初始化时,首先在 VFS 中进行注册。如果文件系统内建于内核中,则
初始化过程发生在系统引导时;如果文件系统作为内核可装载的模块,则实际挂装某个
文件系统时进行初始化。当某种基于块设备的文件系统(包括 root 文件系统)被挂装
,VFS 必须读取其超块。不同类型的文件系统所对应的超块读取例程必须能够理解实际
文件系统的拓扑结构,并且能够将实际的超块结构映射为 VFS 超块结构。每个 VFS 超
块包含了文件系统信息,并且还包含一些完成特定功能的函数指针。例如,某个超块代
表一个已挂装的 Ext2 文件系统,则超块中包含有专门读取 Ext2 文件系统索引节点的
函数地址。每个 VFS 超块中包含指向实际文件系统第一个 VFS 索引节点的指针。对于
root 文件系统而言,第一个索引节点就是代表“/”目录的节点。对 Ext2 文件系统
来说,这种映射关系非常高效,而对其他文件系统则略微有些低效。
进程在访问目录和文件的过程中,会调用系统例程对 VFS 节点进行遍历。因为每个文
件和目录均由一个索引节点表示,因此,有相当多的索引节点会被重复访问。由于这一
原因,为了提高索引节点的访问速度,VFS 将这些节点保存在索引节点高速缓存中。如
果某个节点当前不在高速缓存中,则调用专用于某种文件系统的索引节点读取例程,以
便读取适当的索引节点。读取的索引节点会保存在高速缓存中,而较少使用的 VFS 索
引节点会从高速缓存中剔除。
VFS 同时维护一个目录高速缓存,以便能够快速找到频繁使用的目录索引节点。目录高
速缓存并不保存目录本身的索引节点,这些索引节点应当保存在索引节点高速缓存中;
目录高速缓存实际保存的是完整目录名到对应索引节点编号的映射关系。
所有的 Linux 文件系统使用共同的缓冲区高速缓存,所有的文件系统以及 Linux 内核
使用同一个缓冲区高速缓存。缓冲区高速缓存不依赖于任何文件系统,它和 Linux 内
核用来分配、读取和写入数据缓冲区的机制集成在一起。Linux 文件系统独立于底层介
质以及设备驱动程序而存在,这种机制具有非常明显的优点。所有的块设备在 Linux
内核中注册,并且提供一致的、基于块的、异步的接口。当文件系统从物理介质中读取
数据时,实际是向控制该设备的驱动程序发送读取物理数据块的请求。缓冲区高速缓存
负责将块设备驱动程序的接口集成在一起。文件系统在读取数据块时,这些块被保存在
全局的缓冲区高速缓存中。缓冲区高速缓存中的缓冲区由唯一的设备标识符以及块编号
标识,当进程频繁访问相同的数据时,这些数据就可以直接从高速缓存中读出,而不必
进行实际的磁盘读取操作。
13.2.1 VFS 超块
每个已挂装的文件系统由一个 VFS 超块代表。VFS 超块中包含的信息由表 13-5 列出
。
表 13-5 VFS 超块中的信息
设备
包含文件系统的块设备标识符。对于 /dev/hda1,其设备标识符为 0x301。
索引节点指针
这里包含两个索引节点指针。mounted 索引节点指针指向文件系统的第一个节点;co
vered 指针指向代表文件系统挂装目录的节点。root 文件系统的 VFS 超块没有 cove
red 指针。
数据块大小
文件系统中数据块的大小,以字节为单位。
超块操作集
指向一组超块操作例程集的指针。这些例程由 VFS 用来读取和写入索引节点以及超块
。
文件系统类型
指向文件系统的 file_system_type 数据结构的指针。
文件系统的特殊信息
该文件系统的特殊信息。
13.2.2 VFS 索引节点
和 Ext2 文件系统一样,VFS 中的文件、目录等均由对应的索引节点代表。每个 VFS
索引节点中的内容由文件系统专属的例程提供。VFS 索引节点只存在于内核内存中,实
际保存于 VFS 的索引节点高速缓存中。VFS 索引节点中的信息由表 12-6 给出。
表 12-6 VFS 索引节点中的数据域
设备
包含该文件或其他任何 VFS 索引节点所代表的对象的设备标识符。
索引节点编号
索引节点编号,在文件系统中唯一。设备和索引节点的组合在 VFS 中唯一。
模式
VFS 索引节点代表的对象类型,以及相应的访问权限。
用户标识符
所有者标识符。
时间
建立、修改和写入的时间。
块大小
数据块的大小,以字节为单位。
索引节点操作集
指向索引节点操作例程集的指针。这些例程专用于该文件系统,用来执行针对该节点
的操作。
计数
系统组件使用该 VFS 节点的次数。计数为零,表明该节点可丢弃或重新使用。
锁定
该数据域用来锁定 VFS 节点。
脏
表明 VFS 节点是否被修改过,若如此,该节点应当写入底层文件系统。
文件系统的特殊信息
和文件系统相关的特殊信息。
13.2.3 文件系统的注册
图 13-6 注册的文件系统
如前所述,对特定文件系统的支持可内建于 Linux 内核之中,也可以以可装载模块的
形式动态装载。如果内建于内核,则系统启动时进行文件系统的注册和初始化,否则在
模块装载时注册,而在模块卸载时注销。每个文件系统的初始化例程在 VFS 中注册,
并由一个 file_system_type 数据结构代表,其中包含了文件系统的名称以及一个指向
对应 VFS 超块读取例程的地址。图 13-6 所示是内核中的 file_system_type 链表,
链表头由 file_systems 指定。每个 file_system_type 结构成员由表 13-7 给出。
表 13-7 file_system_type 结构成员
超块读取例程
read_super
当属于该文件系统类型的实际文件系统被挂装时,VFS 调用该例程读取超块。
文件系统名称 name
文件系统的名称。例如 Ext2、vfat、iso9660等。
要求设备
requires_dev
该文件系统是否需要设备支持?并不是所有的文件系统都需要特定设备,例如,/pro
c 文件系统并不需要块设备。
查看 /proc 文件系统的 filesystems 文件内容,可了解当前注册的文件系统类型:
$ cat /proc/filesystems
Ext2
msdos
nodev proc
13.2.4 文件系统的挂装和卸装
用户(一般是 root)在挂装文件系统时,要指定三种信息:文件系统的名称、包含文
件系统的物理块设备和文件系统在已有文件系统中的挂装点。例如:
$ mount -t iso9660 /dev/hdc /mnt/cdrom
虚拟文件系统对上述命令的执行过程如下:
1. 寻找对应的文件系统信息。VFS 通过 file_systems 在 file_system_type 组成的
链表中根据指定的文件系统名称搜索文件系统类型信息。
2. 如果在上述链表中找到匹配的文件系统,则说明内核具有对该文件系统的内建支持
。否则,说明该文件系统可能由可装载模块支持,VFS 会请求内核装入相应的文件系统
模块,此时,该文件系统在 VFS 中注册并初始化。
3. 不管是哪种情况,如果 VFS 无法找到指定的文件系统,则返回错误。
4. VFS 检验给定的物理块设备是否已经挂装。如果指定的块设备已被挂装,则返回错
误。
5. VFS 查找作为新文件系统挂装点的目录的 VFS 索引节点。该 VFS 索引节点可能在
索引节点高速缓存中,也有可能需要从挂装点所在的块设备中读取。
6. 如果该挂装点已经挂装有其他文件系统,则返回错误。因为同一目录只能同时挂装
一个文件系统。
7. VFS 挂装代码为新的文件系统分配超块,并将挂装信息传递给该文件系统的超块读
取例程。系统中所有的 VFS 超块保存在由 super_blocks 指向的 super_block 数据结
构指针数组中。
8. 文件系统的超块读取例程将对应文件系统的信息映射到 VFS 超块中。如果在此过
程中发生错误,例如所读取的超块幻数和指定的文件系统不一致,则返回错误。
9. 如果成功挂装,则所有已挂装的文件系统形成如图 13-7 所示的数据结构。
图 13-7 已挂装的文件系统数据结构
从图 13-7 中可看到,每个已挂装的文件系统由 vfsmount 结构描述,所有的 vfsmou
nt 结构形成了一个链表,该链表头由 vfsmntlist 指针代表。系统中还有两个指针,
vfsmnttail 和 mru_vfsmnt,分别指向链表尾和最近使用过的 vfsmount 结构。每个
vfsmount 结构包含保存该文件系统的块设备号,文件系统挂装点的目录名称,以及指
向为该文件系统分配的 VFS 超块的指针。而 VFS 超块中则包含描述文件系统的 file
_system_type 结构指针和该文件系统根节点指针。
如果文件系统中的文件当前正在使用,该文件系统是不能卸装的。如果文件系统中的文
件或目录正在使用,则 VFS 索引节点高速缓存中可能包含相应的 VFS 索引节点。检查
代码在索引节点高速缓存中,根据文件系统所在设备的标识符,查找是否有来自该文件
系统的 VFS 索引节点,如果有且标志为“脏”,则说明该文件系统正在被使用,因此
,文件系统不能被卸装。否则,查看对应的 VFS 超块。如果该文件系统的 VFS 超块标
志为“脏”,则必须将超块信息写入实际的文件系统。上述过程结束之后,对应的 VF
S 超块被释放,vfsmount 数据结构从 vfsmntlist 链表中断开并释放。
13.2.5 VFS 中文件的定位
为了在虚拟文件系统中定位给定文件的索引节点,VFS 必须每次分解文件路径名中的一
个目录名,并依次搜索每个中间目录名的 VFS 索引节点。因为 VFS 在 VFS 超块中记
录了每个文件系统根的 VFS 索引节点,因此搜索过程从根索引节点开始,具体过程和
Ext2 文件系统的文件定位过程类似。在搜索目录索引节点时,VFS 在目录高速缓存中
搜索,如果能够在目录高速缓存中找到对应的索引节点编号,则在索引节点高速缓存中
搜索索引节点,否则从底层文件系统中获取。
13.2.6 VFS 索引节点高速缓存
VFS 索引节点实际是一个哈希表(或散列表),哈希表的每个入口包含一个 VFS 索引
节点链表的头指针,该链表中的每个 VFS 索引节点具有相同的哈希值。节点的哈希值
根据文件系统所在块设备的标识符,以及索引节点的编号计算得到。当虚拟文件系统要
访问某个节点时,它首先在 VFS 索引节点中搜索。每个要搜索的 VFS 索引节点可计算
得到一个对应的哈希值,然后,VFS 将该哈希值作为访问哈希表的索引,如果对应的哈
希表入口指向的索引节点链表中包含有要搜索的索引节点(相同的设备标识符和相同的
节点编号),则说明该索引节点包含在高速缓存中,这时,找到的索引节点访问计数加
1,否则,必须寻找一个空闲的 VFS 索引节点,并从底层文件系统中读取该索引节点
。VFS 可选择多种方法获取空闲索引节点。如果系统能够分配更多的 VFS 节点,则 V
FS 从内核中分配内存页并将其划分为新的空闲节点,然后放在节点列表中。系统所有
的 VFS 节点保存在由 first_ionde 指向的链表中。如果系统已经具备了所有可利用的
索引节点,则必须寻找一个可重复利用的索引节点。当前使用计数为 0 的索引节点可
作为重复利用的节点,对文件系统的根节点来说(以及其他重要的节点),其使用计数
始终大于 !0,因此不会被重复使用。如果找到可重复利用的索引节点,则必须在使用
之前清除该节点。如果该节点标志为“脏”,则需要写回到实际的文件系统中;如果该
节点被锁定,则必须等待解锁。
无论利用哪种方法获得了空闲的 VFS 节点,VFS 会调用文件系统专有的例程从底层文
件系统中读取信息并填充该索引节点。在该节点被填充时,这一新的 VFS 节点的使用
计数为 1,同时被锁定,以免其他进程访问该节点。填充之后,节点被解锁。
为了获得实际使用的 VFS 节点,文件系统可能要访问许多其他的节点。读取目录内容
时,只有最后一个目录的索引节点是实际要使用的,但同时必须读取中间目录的索引节
点。
13.2.7 VFS 目录高速缓存
为了加速对频繁目录的访问过程,VFS 维护一个目录项高速缓存。实际文件系统读取目
录之后,该目录项的细节添加到目录高速缓存中。下次搜索相同的目录项时,可从高速
缓存中快速得到。但是,只有名称相对较短的目录项(15 个字符长度)才被高速缓存
。
目录高速缓存页由一个哈希表组成。哈希值由保存文件系统的设备号以及目录名称计算
得到,该值作为访问哈希表的索引。
为了保证缓存的有效性并及时更新,VFS 保持一组“最近使用 (LRU)”目录缓存项表
。当某个目录项第一次放入高速缓存时,被添加到第一级 LRU 表的后面。当高速缓存
全部占用时,该操作会取代第一级 LRU 表前面的已有项。当该目录再次被访问时,该
目录项自动提升到第二级目录项缓存列表的后面,同样会取代第二级缓存列表前面的二
级缓存目录项。这样,处于各个缓存表前面的项实际是最近没有被访问的目录项,否则
,它们应当处于各目录项列表的后面。
13.3 缓冲区高速缓存
如前所述,所有的 Linux 文件系统使用共同的缓冲区高速缓存。所有块设备的读取和
写入请求通过标准的内核例程调用,以 buffer_head 数据结构的形式提交给设备驱动
程序(参见本书 12.5.3 小节)。buffer_head 中包含了唯一标识块所在设备的设备标
识符,以及要读取或写入的块编号。为了加速对物理块设备的访问过程,Linux 使用了
缓冲区高速缓存。在高速缓存中,任意给定的时间内,存在着来自不同物理设备的数据
块,而且处于不同的状态。任何用来从块设备中读取或写入块设备的块缓冲区,都要经
过缓冲区高速缓存。随着时间的流逝,缓冲区占用的缓存可能让给其他更加需要缓存的
缓冲区,也有可能因为频繁访问而保持在缓存中。
图 13-8 缓冲区高速缓存的哈希表结构
缓冲区高速缓存分为两个功能部分。第一部分是空闲块缓冲区表,每个系统支持的缓冲
区大小对应一个表,系统的空闲块缓冲区在第一次创建和最后被丢弃时在这些表中排队
。当前 Linux 所支持的缓冲区大小为 512、1024、2048、4096 和 8192 字节。第二部
分是高速缓存本身,和 VFS 索引节点高速缓存类似,缓冲区高速缓存也是一个具有相
似结构的哈希表。哈希索引由块所在的设备标识符以及块编号计算得到。图 13-8 是缓
冲区高速缓存的哈希表结构。块缓冲区可能在某个空闲缓冲区表中,也有可能在高速缓
存中。如果块缓冲区在高速缓存中,则这些缓冲区也在 LRU 表中排队。每个缓冲区类
型具有一个 LRU 列表,当前 Linux 支持的缓冲区类型见表 13-8。
表 13-8 Linux 的缓冲区类型
干净的
未使用的、新的缓冲区。
锁定的
当前锁定的缓冲区,正等待写入。
脏的
脏的缓冲区。其中包含新的有效数据,但尚未写入块设备。
共享的
共享的缓冲区。
非共享的
曾经被共享,但当前不再共享的缓冲区。
当文件系统需要从底层物理设备上读取数据块时,它首先试图从缓冲区高速缓存中读取
。如果无法从缓冲区高速缓存中得到数据块,它会从适当大小的空闲缓冲区表中获得一
个干净的缓冲区,并将该缓冲区放入缓冲区高速缓存。如果所需的缓冲区当前在高速缓
存中,该缓冲区可能是经过更新的,也有可能不是。如果未经更新,或该缓冲区是新的
块缓冲区,则文件系统必须请求设备驱动程序读取适当的数据块。
和其他的高速缓存一样,Linux 也需要维护缓冲区高速缓存,以便能够高效运行,并为
不同块设备公平分配缓存项。为此,Linux 利用 bdfush 内核守护进程完成一些常务工
作,而其他一些工作则随着缓存的使用而自动完成。
13.3.1 bdflush 内核守护进程
bdflush 内核守护进程是一个简单的内核线程,它可以动态地对系统中具有许多“脏”
缓冲区的情形作出响应。它作为系统线程在系统启动时运行,它在系统中注册的进程名
称为 kflushd。通常情况下,该进程处于休眠状态,在如下情况下,该进程被唤醒:
分配和丢弃缓冲区时,如果系统检测到脏缓冲区的比例达到指定值。默认情况下该值为
60%。 系统急需缓冲区时。
利用 update 命令,可查看并控制许多和该守护进程相关的值:
# update -d
bdflush version 1.4
0: 40 Max fraction of LRU list to examine for dirty blocks
1: 500 Max number of dirty blocks to write each time bdflush activated
2: 64 Num of clean buffers to be loaded onto free list by refill_freelist
3: 64 Dirty block threshold for activating bdflush in refill_freelist
4: 15 Percentage of cache to scan for free clusters
5: 3000 Time for data buffers to age before flushing
6: 500 Time for non-data (dir, bitmap, etc) buffers to age before flushin
g7: 1884 Time buffer cache load average constant
8: 2 LAV ratio (used to determine threshold for buffer fratricide).
#
当缓冲区因为写入数据而变脏时,这些缓冲区被链接到 BUF_DIRTY LRU 表中。bdflus
h 会试图将合理数量的脏缓冲区写入磁盘,这一数量默认为 500,该参数也可以通过
update 命令查看和修改。
13.3.2 update 进程
update 不仅是一个命令,而且也是一个守护进程。该守护进程一般在系统初始化时以
超级用户身份运行,它周期性地把所有老的脏缓冲区刷新到磁盘上。它通过调用一个类
似 bdflush 的系统服务完成刷新任务。当缓冲区变脏时,系统以系统时间作标记,表
明该缓冲区应当刷新到磁盘。每次 update 运行时,它在系统的脏缓冲区中查找已到达
刷新时间的缓冲区,每个到达刷新时间的缓冲区都会被写入磁盘。
13.4 /proc 文件系统
和其他文件系统不同的是,/proc 文件系统并不是一个真正存在于块设备上的文件系统
。这正表明了 Linux 虚拟文件系统的强大功能。在系统的初始化过程中,/proc 文件
系统在 VFS 中注册。当 VFS 需要打开其中的目录或文件时,它利用内核信息建立这些
文件和目录。
/proc 文件系统为用户提供了一个查看内核内部工作情况的窗口。
13.5 特殊设备文件
和所有的 UNIX 版本一样,Linux 利用特殊文件代表系统的硬件设备。设备文件实际并
不占用任何文件系统的空间,它只是作为访问设备驱动程序的入口。Ext2 文件系统和
VFS 文件系统将设备文件作为特殊索引节点实现。设备分为块设备和字符设备两种。
设备文件的索引节点中包含对应设备的主设备号和次设备号,分别标识设备类型以及该
设备在同类设备中的编号。
13.6 相关系统工具和系统调用
13.6.1 Linux 支持的文件系统
由于 Linux 采用了虚拟文件系统,从理论上讲,Linux 可以支持所有的文件系统。表
13-9 给出了 Linux 所支持的重要文件系统。
表 13-9 Linux 支持的重要文件系统
MINIX
MINIX 操作系统使用的文件系统,Linux 最初使用该文件系统开发。是最老的、也是
最可靠的文件系统。但该文件系统的能力受到一定限制,例如,省略了某些时间戳,文
件名程度不能超过 30 个字符等。
xia
MINIX 文件系统的更新版,突破了部分限制,但没有新的功能特色。
Ext2
功能强大的 Linux 土生土长的文件系统。
ext
Ext2 文件系统的老版本。该文件系统可转换到 Ext2 文件系统。
msdos
和 MS-DOS 兼容的 FAT 文件系统。
umsdos
对 Linux 中 msdos 文件系统驱动程序的扩展,以便能够支持长文件名、所有者、许
可、链接和设备文件。它使得通常的 msdos 文件系统可以当作 Linux 固有的文件系统
一样使用。
vfat
Microsoft 对原 fat 文件系统的扩展,可以支持长文件名。
iso9660
该文件系统是标准的 CD-ROM 文件系统。对该标准更加流行的 Rock Ridge 扩展允许
长文件名的自动支持。
nfs
允许在多台计算机之间共享文件系统的网络文件系统。
hpfs
高性能文件系统,是 OS/2 的文件系统。
ntfs
Windows NT 的文件系统。
sysv
System V/386、Coherent 和 Xenix 的文件系统。
在多个操作系统共存的系统中,有时难免使用除 Ext2 之外的文件系统。如果没有这方
面的需求,则应当使用 Ext2 文件系统,因为 Ext2 和 VFS 之间最接近,从而可获得
最高的性能。
13.6.2 建立文件系统
利用 mkfs 命令建立,或初始化文件系统。实际上,每个文件系统类型对应有自己单独
的初始化命令。mkfs 只是最为一个前台的程序而存在,它实际根据要建立的文件系统
类型调用相应的命令,文件系统类型由 mkfs 命令的 -t 参数指定。其他常用的 mkfs
命令参数如下:
-c 检查坏块并建立相应的坏块清单
-l filename 从指定的文件 filename 中读取初始坏块。
如果要在软盘上建立一个 Ext2 文件系统,可利用如下的命令:
$ fdformat -n /dev/fd0H1440
Double-sided, 80 tracks, 18 sec/track. Total capacity 1440 kB.
Formatting ... done
$ badblocks /dev/fd0H1440 1440 > bad-blocks
$ mkfs -t Ext2 -l bad-blocks /dev/fd0H1440
mke2fs 0.5a, 5-Apr-94 for Ext2 FS 0.5, 94/03/10
360 inodes, 1440 blocks
72 blocks (5.00%) reserved for the super user
First data block=1
Block size=1024 (log=0)
Fragment size=1024 (log=0)
1 block group
8192 blocks per group, 8192 fragments per group
360 inodes per group
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
$
上述命令首先利用 fdformat 格式化软盘,然后利用 badblocks 命令检查坏块,并将
坏块清单重定向输出到文件 bad_blocks 中。最后,利用 mkfs 命令建立文件系统,b
ad_blocks 文件作为初始的坏块清单。
如果利用 mkfs 命令的 -c 参数,可省略 badblocks 命令。但文件系统建立之后,仍
有必要利用 badblocks 命令进行检查。
13.6.3 文件系统的挂装和卸装
通常来讲,文件系统的挂装和卸装只能由超级用户完成。挂装命令 mount 可如下使用
:
# mount -t vfat -r /dev/hda5 /mnt/dosd
#
上述命令指定了要挂装的文件系统类型,文件系统所在设备以及挂装点,并将文件系统
挂装为只读。如果要在系统启动时自动挂装文件系统,则可以在 /etc/fstab 文件中指
定。有关内容可参见 fstab 手册页。
利用 umount 命令卸装文件系统,该命令只需一个参数,即希望卸挂的设备或挂装点:
# umount /mnt/dosd
#
或
# umount /dev/hda5
#
13.6.4 检查文件系统的完整性
利用 fsck 命令可检查文件系统中存在的错误,如果需要,可强制该命令修改错误。一
般而言,文件系统经过了大量严格周密的测试,因此在正常使用情况下不会导致错误,
除非因为掉电、硬件失效或操作者的失误而导致文件系统被破坏。
对于系统启动时自动挂装的文件系统,系统会自动进行检测,但是在如下情况下,不进
行彻底的文件系统检测:存在 /etc/fastboot 文件;对 Ext2 文件系统,尚未达到最
大挂装计数。其他文件系统则需要手工检测,例如软盘。
fsck 只能检查尚未挂装的文件系统,这主要是因为该命令需要以原始方式读写磁盘。
13.6.5 检查磁盘错误
应当周期性地检查磁盘上的坏块。利用 badblocks 命令可完成坏块的检查。badblock
s 会报告所有的坏块清单,将该清单传递给 fsck 后,可避免文件系统在坏块上保存数
据。badblocks 和 fsck 命令的组合使用方法如下例所示:
$ badblocks /dev/fd0H1440 1440 > bad-blocks
$ fsck -t Ext2 -l bad-blocks /dev/fd0H1440
Parallelizing fsck version 0.5a (5-Apr-94)
e2fsck 0.5a, 5-Apr-94 for Ext2 FS 0.5, 94/03/10
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Check reference counts.
Pass 5: Checking group summary information.
/dev/fd0H1440: ***** FILE SYSTEM WAS MODIFIED *****
/dev/fd0H1440: 11/360 files, 63/1440 blocks
$
13.6.6 碎片化问题
我们提到过,Ext2 文件系统可通过特定的数据块分配方法来避免磁盘的过分碎片化。
但是,经常性的文件删除和创建操作不可避免会产生碎片,对 Ext2 文件系统,可利用
专用工具进行整理。在 MS-DOS 或 Windows 95 中,有大量工具可用于 FAT 或 VFAT
文件系统的整理。在整理文件系统之前,应当首先备份磁盘上的数据,以免在整理过程
中发生不可恢复的错误。
13.6.7 其他文件系统工具
df 命令可列出所有已挂装文件系统中的空闲空间。
sync 命令用于将缓冲区高速缓存中的脏数据块刷新到磁盘上。
对于 Ext2文件系统,还有一些特殊的工具。tune2fs 可调整 Ext2 文件系统的参数,
其中包括最大挂装计数、两次检查之间的最大时间间隔、为 root 用户保留的数据块个
数等;dumpe2fs 可显示大部分来自 Ext2 文件系统超块的信息;debugfs 是文件系统
调试器,可用来直接访问文件系统中的数据;dump 和 restore 分别用于 Ext2 文件系
统的备份和还原。
13.6.8 系统调用
表 13-10 简要列出了和文件系统相关的系统调用。标志列中各字母的意义可参见表 1
0-1 的说明。
表 13-10 相关系统调用
系统调用
说明
标志
bdflush
将脏缓冲区刷新到磁盘
-c
chdir
改变工作目录
m+c
chmod
修改文件属性
m+c
chown
修改文件所有权
m+c
chroot
设置新的根目录
mc
close
关闭文件
m+c
creat
创建文件
m+c
dup
建立某文件描述符的复本
m+c
dup2
复制某文件描述符
m+c
fchdir
改变工作目录
fchmod
见 chmod
mc
fchown
修改文件的所有权
mc
fclose
关闭文件
m+!c
fcntl
文件/描述符控制
m+c
flock
修改文件锁定
m!c
fpathconf
获取文件信息
m+!c
fread
读取二进制文件流数据
m+!c
fstat
获取文件状态
m+c
fstatfs
获取文件系统状态
mc
fsync
将文件高速缓存写入磁盘
mc
ftime
获取自1970.1.1以来的时区+秒数
m!c
ftruncate
修改文件大小
mc
fwrite
向文件流中写入二进制数据
m+!c
getdtablesize
获取文件描述符表的大小
m!c
ioctl
操作某个字符设备
mc
ioperm
设置某个 I/O 端口的许可
m-c
iopl
设置所有I/O 端口的许可
m-c
link
建立已有文件的硬链接
m+c
llseek
大文件的 lseek
-
lseek
修改某文件描述符的当前位置指针
m+c
lstat
获取文件状态
mc
mkdir
建立目录
m+c
mknod
建立设备
mc
modify_ldt
读取或写入局部描述符表
-
mount
挂装一个文件系统
mc
open
打开一个文件
m+c
pathconf
获取文件的相关信息
m+!c
read
从文件中读取数据
m+c
readv
从文件中读取数据块
m!c
readdir
读取目录
m+c
readlink
获取符号链接的内容
mc
rename
移动/重命名文件
m+c
rmdir
删除某个空目录
m+c
select
进程休眠,直到某个文件描述符上有操作为止。
mc
setfsgid
设置文件系统的组标识符
setfsuid
设置文件系统的用户标识符
setup
初始化设备并安装 root
-
stat
获取文件状态
m+c
statfs
获取文件系统状态
mc
symlink
建立文件的符号链接
m+c
sync
内存和磁盘缓冲区之间的同步
mc
sysfs
获取已配置的文件系统信息
truncate
修改文件大小
mc
ulimit
获取/设置文件限制
c!
umask
设置文件的建立掩码
m+c
umount
卸挂文件系统
mc
unlink
空闲时删除文件
m+c
utime
修改索引节点的时间项
m+c
write
向文件写入数据
m+c
writev
向文件写入数据块
m!c
--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: bin@mtlab.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:209.809毫秒