第14章 文件系统

在第4章、第5章和第13章中,我们介绍了文件I/O,特别是普通磁盘文件。在本节和接下来的章节中,我们会深入探究一系列文件相关的主题:

  • 本章主要探究文件系统
  • 第15章主要阐述各种与文件相关的属性,包括时间戳、所属权和权限
  • 第16章和17章介绍Linux2.6的两个新特性:扩展属性(extended attributes)访问控制列表(ACLs)。扩展属性可将任意元数据与一文件关联,而ACL则是传统UNIX文件权限模型的扩展。
  • 第18章讨论目录和链接。

本章主要与文件系统有关,文件系统是文件和目录的集合。我们将介绍一系列文件系统相关的概念,有时使用传统的Linux ext2文件系统作为具体的例子。此外,本章还会简要介绍一些Linux支持的日志文件系统。
在本章结尾,将会讨论用于挂载(mount)和卸载(unmount)文件系统的系统调用,以及用来获取已挂载文件系统信息的库函数。

14.1 Device Special Files (Devices)(设备专用文件)

本章会频繁地提到 磁盘设备(disk devices),所以我们首先对设备文件的概念做一个简单的介绍。
设备专用文件(device special file) 对应于系统中的一个设备。在内核中,每种 设备类型 都有对应的一个 设备驱动器 ,用于处理设备的所有I/O请求。设备驱动器(device driver)是内核代码的单元,可执行一系列操作,通常与相关硬件的输入/输出动作相对应。由设备驱动程序提供的API是固定的,包含的操作对应于系统调用open()、close()、read()、write()、mmap()以及ioctl()。每个设备驱动程序所提供的接口一致,这隐藏了每个设备在操作方面的差异,从而满足了I/O操作的通用型(请参见4.2节)
某些设备时真实存在的,比如鼠标、键盘和磁带设备。而另一些设备是虚拟的,亦即并不存在相应的硬件,但内核(通过设备驱动程序)提供一种抽象设备,其所携带的API与真实设备一般无异。
可将设备划分为以下两种类型。

  • 字符设备(character devices) 基于每个字符来处理数据。终端和键盘都是字符设备的例子
  • 块设备(Block devices) 每次处理一块数据。块的大小取决于设备类型,但通常是512字节的倍数。磁盘和磁带设备都属于块设备。

像其他文件一样,设备文件出现在文件系统中,通常在 /dev 目录下。超级用户可以通过使用 mknod 命令创建设备文件,在特权程序中使用mknode()系统调用可以完成相同的任务。

本书不会对mknode()(make file-system i-node创建,文件系统i节点)系统调用做详细介绍,因为该系统调用的用法一目了然,并且如今它的唯一用处是用于创建设备文件,这不是一般应用所需要用到的。当然,也可以使用mknod()创建FIFO(参见44.7节),但最好使用mkfifo()函数来完成该任务。早先,某些UNIX实现会使用mknod()来创建目录,但如今已为mkdir()系统调用所取代。然而,还有一些UNIX实现(Linux不在此列),为了保持向后兼容性,仍然在mknod()中保留了这一能力,详情见mknode(2)手册页。

在早期的Linux版本中,/dev包含系统中所有可能的设备条目,即使这些设备没有真正地连接到系统上。这意味着/dev会包含数以千计的未用设备项,从而导致两个缺点:其一,对于需要扫描该目录内容的应用而言,降低了程序的可执行速度;其二,根据该目录下的内容无法发现系统中实际存在哪些设备。Linux2.6运用了udev程序解决了上述问题。该程序所依赖的sysfs文件系统是装载于/sys下的伪文件系统,将设备和其他内核对象的相关信息导出至用户空间。

Device IDs

每个设备文件都有一个 major ID numberminor ID number。major ID用于识别设备的类别,用于内核为该设备类型查找合适的驱动器。minor ID用于唯一标识该类别中的特定类别。设备文件的major ID和minor ID通过使用 ls -l 命令展示。
设备的major ID和minor ID记录在设备文件的i-node上(在14.4中介绍i-nodes)。每个设备驱动器都会将与之关联的major ID进行注册,使用这种方法建立设备特有文件和设备驱动器之间的关系。内核是不会使用设备文件名来查找驱动器程序的。
在Linux 2.4以及更早的版本中,系统的设备总数受限于这一事实:设备的major ID和minor ID只能用8位数来表示,加之major ID固定不变,且为统一分配(由Linux命名和编号机构分配),使得上述问题更为严重。Linux 2.6采用了更多位数来存放major ID和minor ID(分别是12位和20位),从而缓解了这个问题。

14.2 Disks and Partitions

常规文件和目录一般驻扎在硬件磁盘设备中。(文件和目录还可能存在于其他设备,例如CD-ROMS,flash内存卡和虚拟磁盘中,但是对于当前的讨论,我们主要关心硬件磁盘设备)。下面几节会介绍磁盘的组织方式以及如何对其分区。

14.3 文件系统

文件系统是对常规文件和目录的组织集合。用于创建文件系统的命令是 mkfs。Linux的强项之一便是支持种类繁多的文件系统,如下所示。

  • 传统的ext2文件系统
  • 各种原生(native)UNIX文件系统,比如,Minix、System V 以及BSD文件系统。
  • 微软的FAT,FAT32以及NTFS文件系统。
  • Apple Macintosh的HFS。
  • 一系列网络文件系统
  • 一系列日志文件系统,包括ext3、ext4、Reiserfs、JFS、XFS以及Btrfs。

从Linux的专有文件 /proc/filesystems 中key查看当前为内核所知的文件系统类型。

ext2 文件系统

多年来,ext2是Linux上使用最广泛的文件系统,也是原始Linux文件系统–ext的继任者。近来,随着各种日志文件系统的兴起,对ext2的使用也日趋减少。有时,在介绍通用文件系统概念时,以一款特定的文件系统实现为例会容易一些,出于这一目的,本章将以ext2为例来介绍文件系统。

文件系统结构

文件系统中用于分配空间的基本单元是 逻辑块(logical block),它是文件系统驻扎的磁盘设备上多个连续的物理块。例如,在ext2文件系统上,逻辑块的大小为1024,、2048或4096字节。
Figure 14-1 描述了磁盘分区和文件系统之间的关系,以及一般文件系统的组成。

在这里插入图片描述
文件系统包含以下部分:

  • 引导块(Boot block): 总是作为文件系统的首块。引导块不为文件系统所用,只是包含用来引导操作系统的信息。操作系统虽然只需要一个引导块,但所有文件系统都设有引导块(其中的绝大多数都未使用)
  • 超级块(Super block): 紧随引导块之后的一个独立块,包含与文件系统有关的参数信息,其中包括:
    1. i-node表的容量
    2. 文件系统中逻辑块的大小
    3. 以逻辑块计,文件系统的大小
      不同的文件系统驻扎在相同的物理设备上可以具有不同的类型和大小,以及不同的参数设置(例如块大小)。这是将磁盘分成多个分区的原因。
  • I节点表(I-node table): 文件系统中的每个文件或目录在i-node table中都有唯一的条目。这些条目记录了关于文件的各种信息。在下节中会对I-node进行详细的介绍。i-node table有时也称为i-list。
  • 数据块(Data blocks): 文件系统中的大部分空间都用于数据块,文件和目录驻扎在文件系统的数据块中。

14.4 I-nodes

文件系统的i-node table中为每个驻扎在文件系统中的文件维护中一个i-node(index node的简写)。对I-nodes的标识,是通过i-node table中的顺序位置,以数字表示。ls -li 命令所展示的第一个字段是文件的i-node number (或简写为i-number)。i-node中包含以下信息:

  • 文件类型(例如常规文件、目录、符号链接、字符设备)
  • 文件的属主(也称为user ID 或 UID)
  • 文件的属组(也称为group ID 或 GID)
  • 3类用户的访问权限:属主(有时也称为用户)、属组以及其他用户(属主和属组用户之外的用户)
  • 3个时间戳:对文件的最后访问时间(ls -lu 所显示地时间)、对文件的最后修改时间(也是 ls -l所默认的显示时间),以及文件状态的最后改变时间(ls -lc 所显示的最后改变i节点信息的时间)。值得注意的是,与其他UNIX实现一样,大多数Linux文件系统不会记录文件的创建时间。
  • 指向文件的硬链接数量
  • 文件的大小,以字节为单位
  • 实际分配给文件的块数量,以512字节块为单位。这一数字可能会简单等同于文件的字节大小,因为考虑文件中包含空洞的情形,分配给文件的块数可能会低于根据文件正常大小(以字节为单位)所计算出的块数。
  • 指向文件数据块的指针。

ext2中的I-nodes和数据块指针

像其他UNIX文件系统一样,ext2文件系统没有连续及有序地存储文件的数据块。为了定位这些这些文件数据块,内核在i-node中维护了一组指针。如图14-2
在这里插入图片描述

无需连续存储文件块,使得文件系统对磁盘空间的利用更为高效。特别是,还能降低空闲磁盘空间的碎片化程度,即因众多不连续空闲磁盘碎片(因其空间太小而无法使用)而导致地磁盘空间浪费。换言之,对空闲磁盘的高效利用,是以分配磁盘空间中文件的碎片化为代价的。

在ext2下,每个i-node包含15个指针。前面的12个指针(Figure 14-2中编号为0到11的指针)指向文件前12个块在文件系统中的位置,接下来是一个指向指针块的指针,提供了文件的第13个以及后续数据块的位置。指针块中指针的数量取决于文件系统中块的大小。每个指针需要占用4个字节,因此指针的数量在256(块容量为1024字节)~1024(块容量为4096字节)之间。这样就考虑了大型文件的情况。即便对于巨型文件,第14个指针(Figure 14-2中编号为13)是一个双重间接指针-指向指针块,其块中指针进而指向指针块,此块中指针最终才指向文件的数据块。只要有体量巨大的文件,就会随之产生更深一层的递进:Figure 14-2中i-node的最后一个指针属于三重间接指针。
这一貌似复杂的系统,其设计意图是为了满足多重需求。首先,该系统在维持i节点结构大小固定的同事,支持任意大小的文件。其次,文件系统既可以以不连续的方式来存储文件块,又可以通过lseek()随机访问文件,而内核只需计算所遵循的指针。最后,对于大多数系统中占绝对多数的小文件而言,这种设计满足了对文件数据块的快速访问:通过i-node的直接指针访问,一击必中。
上述设计同样考虑了巨型文件的处理,对于大小为4906字节的块而言,理论上,文件大小可略高于102410241024*4096字节,或4TB(4096GB)。(之所以说“略高于”,是因为指针指向块的方式可以为直接、间接或双重间接。与三重间接指针所指向的范围比,多出来的那些空间实在是微不足道。)
该设计的另一个优点在于文件可以有黑洞(如4.7节所述)。文件系统值需将i节点和间接指针块中的相应指针打上标记(值0),表明这些指针并未指向实际的磁盘块即可,而无需为文件黑洞分配空字节数据块。

14.5 虚拟文件系统(VFS)

Linux 所支持的各种文件系统,其实现细节均不相同。举例来说,这些差异包括文件块的分配方式,以及目录的组织方式。如果每个与文件打交道的程序都需要理解各种文件系统的具体细节,那么编写与各类文件系统交互的程序将是不可能完成的任务。虚拟文件系统(virtual file system,VFS,有时也称为virtual file switch,虚拟文件交换)是一种内核特性,通过文件系统操作创建抽象层来解决上述问题(参见图14-3)。
在这里插入图片描述
VFS背后的原理其实很直白:

  • VFS针对文件系统定义了一套通用接口。所有与文件交互的程序都会按照这一接口来操作。
  • 每种文件系统都会提供VFS接口的实现。

这样一来,程序只需理解VFS接口,而无需过问具体文件系统的实现细节。
VFS接口的操作与涉及文件系统和目录的所有常规系统调用相对应,这些系统调用有open()、read()、wirte()、lseek()、close()、truncate()、stat()、mount()、umount()、mmap()、mkdir()、link()、unlink()、symlink()以及rename() 。
VFS的抽象层建模精确仿照传统的UNIX文件系统模型。

14.6 日志文件系统

ext2文件系统的缺点是:系统崩溃之后,为确保文件系统的完整性,重启时必须对文件系统的一致性进行检查(fsck)。由于系统每次崩溃时,对文件的更新可能只完成了一部分,而文件系统元数据(目录项,i-node信息以及文件数据块指针)也将处于不一致状态,一旦这个问题得不到修复,那么文件系统会遭到进一步破坏,因此上述举措实属必要。如有可能,就必须进行修复,否则将会丢弃那些无法获取的信息。
问题在于,一致性检查需要遍历整个文件系统。如果文件系统较小,只需要几秒钟或者几分钟便可完成。而在大型文件系统上,上述操作可能要数小时。
采用日志文件系统,则无需在系统崩溃后对文件进行漫长的一致性检查。在实际更新元数据之前,日志文件系统会将这些更新操作记录于专用的磁盘日志文件中。对元数据更新的记录是按其相关性分组(以事务的方式记录)进行的。在事务处理过程中,一旦系统崩溃,系统重启时可利用日志重做任何不完整的更新,同时为文件系统恢复一致性状态。系统崩溃之后,即便是超大型的日志文件系统,通常也会在几秒之内复原,因而对于有高可用性需求的系统及其具又吸引力。日志文件系统最大的缺点是增加了文件更新的时间。

某些日志文件系统只会确保文件元数据的一致性。由于不记录文件数据,因此一旦系统崩溃,可能会造成数据丢失。ext3、ext4和Reiserfs文件系统提供了记录数据更新的选项,但若记录的东西过多,则会降低文件I/O的性能。

14.10 虚拟内存文件系统:tmpfs

本章中,我们目前所讨论的所有文件系统都是驻扎在磁盘上的。但是,Linux还支持驻扎在内存中的 虚拟文件系统(virtual file systems) 。对于应用来说,虚拟文件系统看上去就跟其他文件系统一样,在虚拟文件系统上也可以对文件和目录使用相同的操作(open()、read()、write()、link()、mkdir())。但是一个很重要的不同是:虚拟文件系统的文件操作更加快速,因为不涉及磁盘访问。
为Linux开发了各种基于内存的文件系统。其中最复杂的要属 tmpfs 文件系统,它在Linux2.4中首次出现。tmpfs文件系统与其他基于内存的文件系统的不同之处在于 虚拟(virtual) 内存文件系统。也就是说,tmpfs不但使用RAM,而且当RAM用完时还会利用交换空间(swap space)。

tmpfs文件系统是一个可选的Linux内核组件,通过CONFIG_TMPFS选项进行配置。

使用以下命令来创建tmpfs文件系统:

mount -t tmpfs source target

source 可以是任意的名字。

14.12 总结

设备都由/dev下的文件来表示。每个设备都有相应的设备驱动程序,用以执行一套标准的操作,与之对应的系统调用包括open()、read()、write()和close()。设备既可以是实际存在的,也可以是虚拟的,这分别表明了硬件设备的存在与否。无论如何,内核都会提供一种设备驱动程序,并实现与真实设备相同的API。
可以将硬盘划分为一个或多个分区,每个分区都可包含一个文件系统。文件系统是对常规文件和目录的组织集合。Linux实现的文件系统多种多样,其中包括传统的ext2文件系统。ext2文件系统在概念上类似于早期的UNIX文件系统,由引导块、超级块、i-node table和包含文件数据块的数据区域组成。每个文件在文件系统i-node table上都有一条对应记录,记录了与文件相关的各种信息,其中包括文件类型、大小、链接数、所有权、权限、时间戳,以及指向文件数据块的指针。
Linux 还提供了若干日志文件系统,其中包括Reiserfs、ext3、ext4、XFS、JFS以及Btrfs。在实际更新文件之前,日志文件系统会记录元数据更新(还可有选择地记录数据更新和文件系统更新)。这也意味着,一旦系统崩溃,系统可以重放(replay)日志文件,并迅速将文件系统恢复到一致状态。日志文件系统的最大优点在于系统崩溃后,无需像常规UNIX文件系统那样对文件系统进行漫长的一致性检查。
Linux系统上的所有文件系统都挂载于单根目录树之下,其数根目录"/"。目录树中挂载文件系统的位置被称为文件系统挂载点。
特权级进程可以使用mount()和umount()系统调用来挂载、卸载文件系统。可使用statvfs()来获取与已挂载文件系统有关的信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值