Linux文件管理
Linux一个巨大的优势就是它是开源的系统,它提供了对多种文件系统的支持。现代的Linux内核可以支持几乎所有计算机系统使用的文件系统,从基本的FAT支持,到高性能文件系统,比如日志文件系统(JFS)。
Linux文件结构
Linux将文件存储在单个目录结构中,这个目录被称为虚拟目录 (virtual directory)。虚拟目录将安装在PC上的所有存储设备的文件路径纳入单个目录结构中。
Linux虚拟目录结构只包含一个称为根 (root)目录的基础目录。根目录下的目录和文件会按照访问它们的目录路径一一列出。 Linux文件系统结构是从Unix文件结构演进过来的。在Linux文件系统中,通用的目录名用于表示一些常见的功能。
- / 虚拟目录的根目录。通常不会在这里存储文件
- /bin 二进制目录,存放许多用户级的GNU工具
- /boot 启动目录,存放启动文件
- /dev 设备目录,Linux在这里创建设备节点
- /etc 系统配置文件目录
- /home 主目录,Linux在这里创建用户目录
- /lib 库目录,存放系统和应用程序的库文件
- /media 媒体目录,可移动媒体设备的常用挂载点
- /mnt 挂载目录,另一个可移动媒体设备的常用挂载点
- /opt 可选目录,常用于存放第三方软件包和数据文件
- /proc 进程目录,存放现有硬件及当前进程的相关信息
- /root root用户的主目录
- /sbin 系统二进制目录,存放许多GNU管理员级工具
- /run 运行目录,存放系统运作时的运行时数据
- /srv 服务目录,存放本地服务的相关文件
- /sys 系统目录,存放系统硬件信息的相关文件
- /tmp 临时目录,可以在该目录中创建和删除临时工作文件
- /usr 用户二进制目录,大量用户级的GNU工具和数据文件都存储在这里
- /var 可变目录,用以存放经常变化的文件,比如日志文件
虚拟文件系统
虚拟文件系统(Virtual Files System, VFS)是驻留在用户进程与各种类型的Linux文件系统之间的一个抽象接口层。VFS提供了访问文件系统对象的通用对象模型(比如索引节点、文件对象、分页缓存、目录条目,等等)和方法。它对用户进程隐藏了实现每个文件系统的差异。有了VFS,用户进程不需要知道系统使用的是哪一个文件系统,为每个文件系统运行哪一个系统调用。
文件系统日志
在非日志文件系统中,当对一个文件系统执行写操作时,Linux内核首先改变文件系统的元数据(metadata),然后再写实际的用户数据。这个操作有时候会损害数据的完整性。在对文件系统的元数据执行写操作的过程中,如果系统由于某种原因突然崩溃,则文件系统的一致性可能会被破坏。通过检测所有的元数据,fsck进程可以修正不一致,并在下次重新启动时恢复一致性。但是当系统具有一个巨大的卷,且它需要大量的时间来完成时,那么在这个过程中是无法对系统执行操作的。
而日志文件系统可以解决这个问题。在执行写入数据到实际文件系统之前,先将改变数据写入到日志区。日志区域可以放置在文件系统之上或者文件系统之外。被写入日志区域的数据称为日志记录。如果支持,那么它包含了改变的文件系统元数据和实际的文件数据。
Ext2
Ext3文件系统的前身是Ext2文件系统。其是一个快速、简单的文件系统,不像当前大多数其他文件系统,它没有日志功能。
Ext2文件系统以引导扇区开始,接下来是块组。将整个文件系统分成若干个小的块组有助于提高性能,因为它可以将用户数据的索引节点(i-node)表和数据块更紧密地保存在磁盘盘片上,因此可以减少寻道时间。一个块组包括以下项目:
- 超级块(Super Block)。 文件系统的信息存储在这里。超级块的精确副本被放置在每个块组的顶部。
- 块组描述符(Block group descriptor)。 块组上的信息被存储在这里。
- 数据块位图(Data block bitmaps)。 用于空闲数据块管理。
- 索引节点位图(I-node bitmaps)。 用于空闲索引节点的管理。
- 索引节点表(I-node tables)。 索引节点表存储在这里。每个文件有一个相应的索引节点表,其中保存了文件的元数据,比如文件模式、uid、gid、atime、ctime、mtime和到数据块的指针。
- 数据块(Data blocks)。 实际的用户数据存储的位置。
要查找组成一个文件的数据块,首先要查找文件的索引节点。如果一个进程请求打开/var/log/messages,那么kernel会解析文件路径,搜索/(根目录)的目录条目,该目录条目中有它(根目录)下面文件和目录的信息。接下来内核可以找到/var的索引节点,并且查看/var的目录条目,该目录条目中也有/var下面文件和目录的信息。内核以此方式一直向下搜索直到找到文件的索引节点。Linux内核使用一个文件对象缓存,比如目录条目缓存或者索引节点缓存来加速查找符合条件的索引节点。
一旦Linux内核找到了文件的索引节点,它会试图找到实际的用户数据块。正如前面描述的,索引节点有到数据块的指针。通过它,内核可以得到数据块。对于大文件,Ext2实现数据块直接/间接的引用。
Ext3
当前企业级Linux发行版支持Ext3文件系统。这是一种广泛使用的Ext2文件系统的更新版本。它的基本结构非常类似于Ext2文件系统,主要的区别是它支持日志功能。Ext3文件系统的要点包括:
- 可用性(Availability)。 Ext3总是以一种一致性的方式写数据到磁盘,因此在遇到不正常关机(意外电源故障或系统崩溃)时,服务器不需要花费时间检测数据的一致性,从而减小了系统恢复时间。
- 数据完整性(Data integrity)。 通过使用mount命令显式声明日志模式data=Journal,所有数据,包括文件数据和元数据,都被写入日志中。
- 速度(Speed)。 显式声明了日志模式data=wirteback,你可以根据你的商业需求决定写入速度与完整性。在繁重的同步写环境下这一点值得注意。
- 灵活性(Flexibility)。 可以简单地对现有的Ext2文件系统升级,并且没有重新格式化的必要。通过执行tune2fs命令和编辑/etc/fstab文件,你可以轻易地将一个Ext2系统更新到一个Ext3文件系统。还要注意,Ext3文件系统可以同Ext2禁用日志那样被挂载。
日志模式
Ext3文件系统支持3种类型的日志模式。
- 全日志(journal)。 在全日志方式中,元数据和实际数据均先被写入日志区,然后再写入主文件系统。 该方式提供了很高的安全性,不论系统在元数据被写入日志区阶段还是在实际数据被写入日志区阶段发生崩溃,均不影响实际的主文件系统。但该模式为实现安全性也付出了效率的代价,因为所有数据都要写入两次。
- 顺序(ordered)。 在顺序方式中,实际数据同样被直接写入主文件系统,而元数据在实际数据写入完成后才被写入日志区。顺序模式是日志文件系统默认使用的模式。 假如实际数据被写入过程中出现系统崩溃,那么修复文件系统后,元数据不变,部分实际数据被更新。
- 回写(writeback)。 在回写方式中,元数据被记录到日志区中,实际数据被直接写入到主文件系统。该方式能提供较好的性能。 使用回写模式,假如元数据被写入日志区后出现系统崩溃,那么在对文件系统进行恢复后,元数据和部分实际数据被更新,但恢复后的文件可能包含原先旧的垃圾数据。
以上三种模式共同的特点是,元数据在被写入主文件系统之前,都先记录到日志区,这样就保证了主文件系统的元数据不被破坏。另外,当进行文件系统恢复时,只需读取日志区中的信息即可进行恢复,而无须遍历整个文件系统。
Ext4
Ext4文件系统引入了大量新功能,但最重要的是对Ext3文件系统的向后和向前兼容性以及在时间戳上的改进。这些改进都立足于提高未来的Linux系统的性能。
- 向后和向前兼容性 由于Ext3是Linux上最受欢迎的文件系统之一,因此其应该能够被轻松迁移到Ext4。为此,Ext4被设计为具有向后和向前兼容性。Ext4对Ext3是向前兼容的,这样就可以将Ext3文件系统挂载为Ext4文件系统。为了充分利用Ext4的优势,必须实现文件系统的迁移,以转换和利用新的Ext4格式。还可以将Ext4挂载为Ext3(向后兼容),但前提是Ext4文件系统不能使用区段。 除了兼容性特性之外,还可以逐步地将Ext3文件系统迁移到Ext4。这意味着没有移动的旧文件可以保留Ext3格式,但新的文件(或已被复制的旧文件)将采用新的Ext4数据结构。可以通过这种方式在线将Ext3文件系统迁移到Ext4文件系统。
- 提高时间戳分辨率和扩展范围 令人惊讶的是,Ext4之前的扩展文件系统的时间戳都是以秒为单位的。这已经能够应付大多数设置,但随着处理器的速度和集成程度(多核处理器)不断提升,以及Linux开始向其他应用领域发展(比如高性能计算),基于秒的时间戳已经不够用。Ext4在设计时间戳时考虑到未来的发展,它将时间戳的单位提升到纳秒。Ext4给时间范围增加了两个位,从而让时间长度再延长500年。
- 伸缩性 文件系统未来发展的一个重要方面就是伸缩性,即根据需求进行伸缩的能力。Ext4以多种方式实现了强大的伸缩性,它的伸缩性超越了Ext3,并且在文件系统元数据管理方面开辟了新领域。
- 突破文件系统的限制 Ext4的一个明显差别就是它支持更大的文件系统、文件和子目录。Ext4支持的最大文件系统为1 EB(1000 PB)。虽然根据今天的标准这个文件系统已经非常巨大,但存储空间的消费会不断增长,因此Ext4必须考虑到未来的发展。Ext4支持最大16 TB的文件(假设由4KB的块组成),这个容量是Ext3的8倍。 最后,Ext4也扩展了子目录的容量,将其从32KB扩展到无穷大。这是极端情况,我们还需要考虑文件系统的层次结构,因为它的最大存储容量为1 EB。此外,它的目录索引也被优化为类似于散列B树结构,因此尽管限制更加多,但Ext4支持更快的查找。
- 区段 Ext3分配空间的方式是其主要缺点之一。Ext3使用空闲空间位映射来分配文件,这种方式不是很快,并且伸缩性不强。Ext3的格式对小文件而言是很高效的,但对于大文件则恰恰相反。Ext4使用区段取代Ext3的机制,从而改善了空间的分配,并且支持更加高效的存储结构。区段是一种表示一组相邻块的方式。使用区段减少了元数据,因为区段维护关于一组相邻块的存储位置的信息(从而减少了总体元数据存储),而不是一个块的存储位置的信息。 Ext4的区段采用分层的方法高效地表示小文件,并且使用区段树高效地表示大文件。例如,单个Ext4 inode有足够的空间来引用4个区段(每个区段表示一组相邻的块)。对于大文件(包括片段文件),一个inode能够引用一个索引节点,而每个索引节点能够引用一个叶节点(引用多个区段)。这种持续的区段树为大文件(尤其是分散的文件)提供了丰富的表示方式。这些节点还包含自主检查机制,以阻止文件系统损坏带来威胁。
- 性能 衡量一个新文件系统的最重要指标就是它的根本性能。这常常是最难实现的指标,因为当文件系统变得庞大并且要求实现高可靠性时,将会以损害性能为代价。但是,Ext4不仅解决了伸缩性和可靠性问题,它还提供了各种改善性能的方法。
- 文件级预分配 某些应用程序,比如数据库或内容流,要求将文件存储在相邻的块上。尽管区段能够将相邻块划分为片段,但另一种更强大的方法是按照所需的大小预分配比较大的相邻块(XFS以前就是采用这种方法)。Ext4通过一个新的系统调用来实现这个目的,这个调用将按照特定的大小预分配并初始化文件。然后,就可以写入必要的数据,并为数据提供不错的读性能。
- 延迟块分配 另一个基于文件大小的优化是延迟分配。这种性能优化延迟磁盘上的物理块的分配,直到块被刷入到磁盘时才进行分配。这种优化的关键是延迟物理块的分配,直到需要在磁盘上写这些物理块时才对其进行分配并写到相邻的块。这类似于持久化预分配,唯一的区别是文件系统会自动执行这项任务。不过如果预先知道文件的大小,则持久化预分配是更好的选择。
- 多个块分配 这是最后一个与相邻块相关的优化,即针对Ext4的块分配器。在Ext3中,块分配器的工作方式是每次分配一个块。当需要分配多个块时,非相邻块中可能存在相邻的数据。Ext4使用块分配器修复了这个问题,它能够在磁盘上一次分配多个块。与前面其他优化一样,这个优化在磁盘上收集相关的数据,以实现相邻读优化。 多个块分配的另一个方面是分配块时需要的处理量。记住,Ext3一次只分配一个块。在最简单的情况下,每个块的分配都要有一个调用。如果一次分配多个块,则对块分配器的调用就会大大减少,从而加快分配并减少处理量。
- 可靠性 Ext4文件系统可能会被扩展得比较大,这将导致可靠性问题。但Ext4通过许多自主保护和自主修复机制来解决这个问题。
- 执行文件系统日志校验和 和Ext3一样,Ext4也是一个日志文件系统。日志记录就是通过日记(磁盘上相邻区域的专门循环记录)记录文件系统变更的过程。因此,日志可使对物理存储执行实际变更变得更加可靠,并且能够确保一致性,即使在操作期间出现系统崩溃或电源中断。这样做可以减少文件系统损坏的几率。 但是即使进行日志记录,如果日志出现错误仍然会导致文件系统损坏。为了解决这个问题,Ext4对日志执行校验和,确保有效变更能够在底层文件系统上正确完成。 Ext4支持根据用户需求采用多种模式的日志记录。例如,Ext4支持Writeback模式,它仅记录元数据;或Ordered模式,它记录元数据,但元数据的数据是从日志中写入的;或Journal模式(最可靠的模式),它同时记录元数据和数据。注意,虽然Journal模式是确保文件系统一致性的最佳选择,但它也是最慢的,因为所有数据都要经过日志记录。
- 在线磁盘碎片整理 尽管Ext4增加了一些特性来减少文件系统的碎片(比如将相邻块分配为区段),但随着系统使用时间的增加,碎片是难以完全避免的。因此出现了在线碎片整理工具e4defrag,它可以对文件系统和单个文件执行碎片整理,从而改善性能。在线碎片整理程序是一个简单的工具,它将文件复制到引用相邻区段的新Ext4 inode。 在线碎片整理还可以减少检查文件系统所需的时间(fsck)。Ext4将未使用的块组标记到inode表中,并让fsck进程忽略它们以加快检查速度。当操作系统因内部损坏(随着文件系统变大,这是不可避免的)而检查文件系统时,Ext4的设计方式将能够提高总体的可靠性。
XFS
eXtended File System(XFS)是一种高性能日志文件系统,其最初是由Silicon Graphics, Inc.为IRIX操作系统创建的,后来移植到Linux上。XFS的并行I/O特性为I/O线程、文件系统带宽、文件和文件系统大小提供了高可扩展性,甚至在文件系统跨越很多存储设备的时候。 XFS的一个典型使用案例是跨越多个存储服务器,每个服务器由很多FC连接的磁盘阵列组成,实现几百TB的文件系统。 XFS拥有大量的功能,其适合部署企业级计算环境,它们都需要实现非常巨大的文件系统:
- 在x86_64系统上,XFS支持的最大文件系统和最大文件近8EB。而Redhat仅支持100TB文件系统。
- XFS实现了元数据的日志操作,能在掉电或系统崩溃的情况下保证文件系统的一致性。在提交实际的数据更新到磁盘之前,XFS将文件系统更新异步记录到一个循环缓冲区(日志)。日志可以位于文件系统内部的数据段,或是外部一个单独的设备上,这样可以减少磁盘访问的竞争。如果系统崩溃或掉电,当文件系统重新挂载时会读取日志,重新执行任何挂起的元数据操作,以确保文件系统的一致性。恢复的速度不依赖于文件系统的大小。
- XFS内部被划分为分配组,它们是固定大小的虚拟存储区域。你创建的任何文件和目录可以跨越多个分配组。每个分配组管理自己拥有的inode和可用空间,独立于其他分配组,这提供了I/O操作的扩展性和并行性。如果文件系统跨多个物理设备,则分配组可以通过将底层信道分离到存储组件来优化吞吐量。
- XFS是一个基于范围的文件系统。可以减少文件的分片和文件的分散程度,每个文件的块可以有可变的范围长度,每个范围可以由一个或多个连续的块组成。XFS采用空间分配方案的目的是有效地找到空闲的范围,它可以用于文件系统操作。如果可能的话,文件范围分配的映射被存储在它的inode中。巨大的分配映射被存储在由分配组进行维护的数据结构中。
- 为了最大化吞吐量,可以在底层条带化的基于软件或硬件阵列上创建XFS文件系统,可以使用su和sw参数给mkfs.xfs的-d选项设置每个条带单元的大小和每个条带单元的数量。XFS使用这些信息存储适当的调整数据、inode和日志。在LVM、md和一些硬件RAID上配置,XFS可以自动选择最佳的条带参数。
- 为了减少分片和提高性能,XFS实现了延迟分配,对缓冲区缓存中的数据保留文件系统块,而当操作系统刷新数据到磁盘的时候再分配块。
- XFS支持扩展的文件属性,每个属性值的大小可以高达64KB,并且每个属性可以被分配给任意root或普通用户名称空间。
- 在XFS中,直接I/O实现了高吞吐量。在应用程序和存储设备之间通过DMA可直接执行非缓存I/O,从而利用设备的全部I/O带宽。
- 要支持提供的快照设备,如卷管理器、硬件子系统、数据库,可以使用xfs_freeze命令挂起和恢复一个XFS文件系统的I/O。
- 要在活跃的XFS文件系统中进行单个文件的碎片整理,可以使用xfs_fsr命令。
- 要增长XFS文件系统,可以使用xfs_growfs命令。
- 要备份和恢复一个活跃的XFS文件系统,可以使用xfsdump和xfsrestore命令。
- 当文件系统被挂载时初始化块和inode的使用,XFS支持用户、组和项目的磁盘配额。项目磁盘配额允许设置XFS文件系统中单独目录层级结构的限制,而不用考虑哪些用户或组写访问到目录层级结构。
XFS的局限性
- XFS是一个单节点文件系统,如果需要多节点同时访问则需要考虑使用GFS2文件系统。
- XFS支持16EB文件系统,而Red Hat仅支持100TB文件系统。
- XFS在单线程元数据密集的工作负荷下使用得较少,在单线程创建和删除巨大数量的小文件的工作负荷下,其他文件系统(Ext4)表现得会更好一些。
- XFS文件系统在操作元数据时可能会使用2倍的Ext4 CPU资源,在CPU资源有限的情况下可以研究使用不同的文件系统。
- XFS更适用于特大文件的系统快速存储,Ext4在小文件的系统或系统存储带宽有限的情况下表现得更好
Btrfs
设计Btrfs文件系统是为了满足大型存储子系统的可伸缩性要求。由于Btrfs文件系统在实现中使用了B树,它的名字也因此而来,尽管它不是一个真正的首字母缩写。B树是一种树形数据结构,在这种数据结构中,文件系统和数据库可以有效地访问和更新大的数据块,而不管树生长得有多大。 Btrfs文件系统提供了以下重要的功能:
- Copy-on-write功能允许创建可读可写的快照,可以将一个文件系统回滚到之前的状态,即使是在你从Ext3或Ext4文件系统转换到Btrfs系统之后。
- 校验和功能,确保数据的完整性。
- 透明压缩,节省磁盘空间。
- 透明碎片整理,提高性能。
- 集成的逻辑卷管理允许实现RAID0、RAID1、RAID10配置,并可动态地添加和移除存储容量。
JFS
日志文件系统(JFS)是一个全64-bit的文件系统,其可以支持非常大的文件和分区。JFS最初是由IBM为AIX®开发的,并且现在在general public license(GPL)下是有效的。JFS对于非常大的分区和文件是一个理想的文件系统。在高性能计算(HPC)或数据库环境中会经常看到UFS系统。如果你想学习更多关于JFS的知识,可参考http://jfs.sourceforge.net上的介绍。 注意: 在Novell SUSE Linux Enterprise Server 10上,JFS不再作为一个新的文件系统被支持。
ReiserFS
ReiserFS是一个具有优化磁盘空间使用率和快速故障恢复功能的快速日志文件系统。ReiserFS被开发在很大程度上是因为Novell的帮助。ReiserFS只在Novell SUSE Linux上被商业支持。
Linux文件概述
与其他操作系统相比,Linux的文件系统更为简单统一。Linux的文件是一个简单的字节序列。因此,一个文本文件(由ASCII字符组成的字符流)和一个二进制文件的结构和访问方法对于Linux是一样的,差别仅在文件内容本身,这要由用户程序来解释。
文件是由一系列块(Block)组成,每个块可能含有512、1024、2048或4096个字节,具体由系统实现决定。不同的文件系统的块大小可以不同,但同一个文件系统的块大小是相同的。使用块较大时,由于每次磁盘操作可以传输更多的数据,操作所花的时间较少,所以可以提高磁盘和内存间数据的传输率;但与此同时,块太大时,存储的有效容量也将会下降。
Linux的文件系统通常由4部分组成:引导块、超级块、索引节点表(Innode Table)和数据块。
- 引导块用来存放文件系统的引导程序,用于系统引导或启动操作系统。如果一个文件系统不安放操作系统,它的引导块将为空。
- 超级块用来描述本文件系统管理的资源,它包含空闲索引节点表和空闲数据块表,具体说明文件系统的资源使用情况。
- 索引节点表用来存储文件的控制信息,每个节点对应一个文件。
- 数据块是磁盘上存放数据的磁盘块,包括目录文件和数据。
超级块
超级块用于描述一个文件系统的资源状态,如文件系统的大小、空闲空间位置信息。超级块由如下字段构成:
- 文件系统的规模(如inode数目、数据块数目、保留块数目和块的大小等)。
- 文件系统中空闲块的数目。
- 文件系统中部分可用的空闲块表。
- 空闲块表中下一个空闲块号。
- 索引节点表的大小。
- 文件系统中空闲索引节点表数目。
- 文件系统中部分空闲索引节点表。
- 空闲索引节点表中下一个空闲索引节点号。
- 超级块的锁字段。
- 空闲块表的锁字段和空闲索引节点的锁字段。
- 超级块是否被修改的标志。
- 其他字段。 从上述可以看出,超级块在文件系统存取和管理文件上起着至关重要的作用。其中,锁字段是为了保证互斥操作。在其他字段中标出该文件系统是否完整。如Linux在关机时要求先将缓冲区数据写回文件系统,并拆卸(Umount)该文件系统,如果没有拆卸文件系统就关机,很可能导致数据丢失,所以在安装一个文件系统时会坚持超级块中字段,如果上次没有做拆卸,就要对文件系统做完整性检查(Fsck)。
索引节点(inode)
索引节点是Linux文件系统的最基本的概念。一个文件的控制信息通常由inode给出,每个inode对应着一个文件。在inode中包含文件数据在磁盘上存储的位置信息,还包含存取权限、文件所有者即存取时间等信息。索引节点存储在磁盘上,内核把inode读进内存索引节点来操作它和它所对应的文件。为了便于理解,把存储在磁盘上的inode称为磁盘索引节点,而把它在内存中的映像称为内存索引节点。
磁盘索引节点由如下字段构成。
- 文件类型:文件可以是普通文件、目录文件、链接文件、设备文件、管道文件。
- 文件链接数:记录了引用该文件的目录表项数,即记录了有多少个文件名指向该文件。
- 文件属主标识:指出该文件的所有者ID。
- 文件属主的组标识:指出该文件所有者属组的ID。
- 文件的访问权限:系统将用户分为文件属主、同组用户和其他用户3类,每类用户可能获得对文件一种或几种访问权限。要特别指出的是,目录文件的执行权限是指修改目录的权力。
- 文件的存取时间:包括文件最近一次被修改的时间、最近一次被访问的时间和最近一次修改索引节点的时间。
- 文件的长度:以字节表示的文件长度。
- 文件的数据块指针:对文件操作的当前位置指针。
索引节点中并不包含文件名,文件名信息存放在目录文件中。在系统中定义了stat结构(该结构定义于/usr/include/sys/stat.h文件中)来存放这些信息。
文件类型
在此再具体介绍一下Linux系统中的不同类型的文件。
普通文件
普通文件又称正规文件,是最常见的一类文件,也是最常用的一类文件。其特点是不包含文件系统的结构信息。通常所接触到的文件,包括图形文件、数据文件、文档文件、声音文件等。这种类型的文件按其内部结构又可细分为两个文件类型:文本文件和二进制文件。
- 文本文件:文本文件是以字符(通常是ASCII码)表示的,是以行为基本结构的信息存储方式。
- 二进制文件:二进制文件是按信息在内存中的格式表示的,它通常不能直接查看,而必须使用相应的软件。
目录文件
Linux文件系统的目录是一种文件,在文件名与索引节点之间的转换起到桥梁作用,是树形文件结构的关键。Linux的目录文件其实非常简单,它的主要内容只有两项:文件名和索引节点号,如图3-2所示。
Linux的文件系统对文件管理是通过索引节点来进行的,目录文件只不过提供了文件名和索引节点号之间的转换手段。为了保证文件系统层次的完整性,目录文件是由系统来管理的,用户只能读目录文件,而不允许直接写目录文件。每个目录文件的前两项是两个特设的文件“.”和“..”。其中,“.”对应于该目录文件本身的索引节点,而“..”则对应于其父目录的索引节点。如果一个目录中只包含“.”和“..”文件,则该目录为空目录。
当用户访问某个文件时,系统需要找到它所对应的索引节点。目录文件建立了文件名和索引节点号之间路径的路线。例如,考虑路径“../a/b”,它要从当前目录开始,到达其父目录,再到达其父目录的子目录a,访问那个目录中的名为b的文件。为此,系统要完成如下步骤:
- 检索当前目录的索引节点。
- 通过当前目录的索引节点,找到当前文件,查出父目录“..”。
- 检索“..”的索引节点。
- 通过父目录“..”的索引节点,检索父目录文件,查出文件“a”的索引节点号。
- 检索“a”的索引节点。
- 利用“a”的目录索引节点中的信息,检索“a”目录文件,查到“b”的索引节点号。
- 检索文件“b”的索引节点号。
- 访问文件“b”。
以上步骤看似复杂,实际上内存中有索引节点表,大多数情况下,检索动作主要在内存中即可完成。
链接文件
链接文件是一种特殊的文件。它实际上是指向一个真实存在的文件的链接。如用户要在一个目录文件中使用其他目录文件下的文件时,并不需要将其复制过来,而只需在此目录中建立一个链接文件指向所要调用的文件。在具体使用时,并不会感觉到它们有什么不同。根据链接对象的不同,链接文件又可以细分为硬链接文件和符号链接文件。
设备文件
设备文件是Linux中最特殊的文件。正是由于它的存在,使得Linux系统可以十分方便的访问外部设备。Linux系统为外部设备提供一种标准接口,将外部设备视为一种特殊的文件。用户可以像访问普通文件一样访问外部设备。这就使Linux系统可以很方便地适应不断发展的外部设备。通常Linux系统把设备文件放在/dev目录下。设备文件使用设备的主设备号和次设备号来指定某外部设备。主设备号用于说明设备类型,次设备号用于说明具体设备。例如,以IDE硬盘为第一主盘,它的第3个分区的设备文件就是/dev/hda3。其中,hd是主设备号,a3是次设备号。根据访问数据方式的不同,设备文件又可以细分为两种类型:块设备文件和字符设备文件。
- 块设备文件:块设备文件是以固定长度的块访问数据的。
- 字符设备文件:字符设备文件是以指定字符(通常是1个字符)访问数据的。
大多数外部设备都提供两种访问方式。但对每一种设备来说,都有其最佳的访问方式。
管道文件
管道文件也是一种很特殊的文件。主要用于不同进程间的信息传递。当两个进程间需要进行数据或信息传递时,可以通过管道文件。一个进程将需要传递的数据或信息写入管道的一端;另一进程则从管道的另一端取得所需的数据或信息。通常管道是建立在高速缓存中的。采用先进先出的规定处理其中的数据。可以细分为有名管道和无名管道两种。
文件描述符
对于初次接触Linux的人来说,文件描述符可能有点抽象。下面的例子有助于理解文件描述符的概念。每个人都有名字,不同的人名字还可能是相同的,如果以名字来区分既不统一又容易产生错误;而每个人的身份证号是唯一的,并且长度都相同(18位),因此,以身份证号来进行区分既便于处理,又不会产生错误。对于Linux而言,所有对设备和文件的操作都使用文件描述符来进行,文件描述符就相当于文件的身份证号。文件描述符是一个非负的整数,表示为int类型的对象,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符。当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。
每个进程都可以拥有若干文件描述符,数量多少则依赖于操作系统的实现,Linux中的每个进程可以有1024个文件描述符。每个进程有自己的文件描述符表。文件描述符表的前3项对于一般的进程是固定的且是由系统自动打开的。文件描述符0是标准输入文件,对于一般进程来说是键盘;文件描述符1是标准输出文件,一般是输出到显示器;文件描述符2是标准错误输出文件,一般也是输出到屏幕。用户程序不用执行打开操作就可直接使用。文件描述符0、1、2对应的符号常量分别是STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,它们都定义在头文件<unistd.h>中。