这篇文章是想从一个很高的层次来讨论Linux文件系统的一些概念。我并不打算很具体描述某个文件系统(如EXT4)的工作原理,也不打算成为文件系统命令教程。
  每台通用计算机都需要将各种类型的文件存储在硬盘或类似的东西上,如USB记忆棒。数据需要存储在硬盘的原因有如下两个:

  • 首先,存储在RAM的数据在计算机关掉时会丢失。有非易失的类型的RAM可以在切掉电源后继续保持存储的数据(如记忆棒和固态硬盘里的flash RAM),但是flash RAM要比标准的、易失的RAM(如DDR3及其它相似类型的RAM)贵太多。
  • 其次,即使标准的RAM仍然比硬盘空间贵很多。RAM和硬盘的价格一直在下降,但是按每字节价格计算,RAM仍然时最贵的。基于16GB RAM成本和2TB硬盘的价格,快速计算每byte的价格,表明每单位的RAM大约比硬盘贵71倍。现在常见的RAM每byte成本在$0.0000000043743750左右。

  在非常早期的计算机中,有一种基于CRT屏幕点的内存非常昂贵,每bit大概$1。

图片致谢:原始照片由Rikki Endsley

定义

  你也许听过人们以很多不同的、令人疑惑的方式谈论文件系统。文件系统这个词有多种意思,你必须从谈论或者文档的上下文来辨别正确的意思。
  我将尝试从我所观察到的它在不同场景是如何被使用的来定义“文件系统”的含义。请注意,当我试图去符合标准的官方含义时,我其实是想基于它的各种不同用法去定义这个术语。这些含义将在文章的后续部分更加细致的探讨。

  1. 以(/)根目录开始的整个Linux目录结构。
  2. 某一具体的数据存储格式,如EXT3,EXT4,BTRFS,XFS等。Linux 支持数百种文件系统,包括一些很老的,同时也包括一些最新的。每种文件系统都采用它自己的元数据结构去定义如何存取数据。
  3. 以某种具体的文件系统格式化的、可挂载到Linux文件系统上任意挂载点的分区或者逻辑卷。

文件系统基本功能

  磁盘存储是必需的,这使得它拥有很多有趣且不可避免的细节。很明显,设计文件系统终极功能是给非易失数据存储提供空间。然而,有许多其它重要的功能都是为了满足这一需求。
  所有的文件系统都需要提供一个命名空间,也即是命名和组织方法。它定义怎样给文件命名,具体地文件名长度以及可用的字符子集。 它还定义数据在磁盘上的逻辑结构,比如采用文件目录来组织文件,而不只是将他们所有都放到一起形成一个巨大文件文件簇。
  一旦命名空间定义好之后,给命名空间提供逻辑基础的元数据结构是必不可少的。元数据结构包括用于支持分层目录结构的数据结构;用于决定哪些块空间是已用的以及哪些块是可用的结构;可以维护文件名和目录的结构;文件信息如大小、创建时间、最后修改或访问时间;以及文件数据在磁盘的位置。其它元数据用于存储关于磁盘划分(如逻辑卷以及分区)的高级信息。更高级的原信息以及结构包含了文件系统所在驱动器或者分区的描述信息,但它是和文件系统原信息分开的、独立的。
  文件系统还需要一个应用程序接口(API)提供系统调用,用以操作文件系统对象如文件或者目录。APIs为文件的创建、移动、删除提供支持。还提供一些算法用于决定比如文件该存放在什么地方,这些算法必须考虑一些目标,如速度或者最小化磁盘碎片。
  现代文件系统还提供安全模型,它是一个定义文件和目录访问权限的模式。Linux文件系统安全模型有助于确保用户仅能访问属于自己的文件,而无法访问属于他人或者操作系统的。
最后一个构建块是软件需要去实现所有这些功能。Linux 采用两个部分软件去实现它,这种方式有助于提高系统和编程者效率。



图1. Linux 两部分文件系统软件实现

  文件系统实现第一个部分是虚拟文件系统,它为内核以及开发者提供一个命令集去访问所有类型的文件系统。虚拟文件系统调用各种文件系统所需的设备驱动去和它交互。第二部分是具体的文件系统设备驱动。设备驱动负责向分区或者逻辑卷上具体的文件系统解释标准文件系统命令集。

目录结构

  作为一个有条理的处女座,我喜欢所有的东西都存储在更小、有条理不同组里面而不是一个大的桶里。采用目录有助于存储并定位我想要的文件。目录也被称作文件夹,因为它可以被看作是很多文件放在不同的文件夹里面,类似于物理桌面。
  在Linux及其它许多操作系统,目录可以采用树状层词次结构来构建。在Linux 文件系统标准(FHS)里,Linux目录结构有良好的定义及文档。当访问目录时,当访问目录时,通过使用斜杠(/)连接的顺序地、更深的目录名(如/var/log 和/var/spool/mail)来实现。这些被称为路径。
  下表提供一个简略的列表,列出了标准的、众所周知的、定义好的顶级Linux目录及其用途。

目录 描述
/(根文件系统) 根文件系统时文件系统的顶级目录。它必须包含,在挂载其它文件系统之前,所有用于引导Linux系统的文件。它必须包含用于引导其余文件系统所需的所有的可执行文件和库。在系统引导结束后,其它文件系统被挂载到标准的、定义好的挂载点作为根文件系统的子目录。
/bin /bin目录包含用户的可执行文件
/boot 包含静态引导加载器和可执行内核以及引导Linux计算机所需的配置文件
/dev 包含所有附加到系统上的硬件设备的设备文件。这些并不是设备驱动,而是在计算机上代表每一个设备的文件,便于访问这些设备
/etc 包含宿主机本地系统配置文件
/home 用户文件的主目录,每个用户在/home都有一个子目录
/lib 包含引导系统所需的共享库文件
/media 用于挂载外部可移除媒体设备的挂载点,例如可能链接到主机的USB驱动
/mnt 常规文件系统(不可移除介质)的临时挂载点,管理员修复或者处理文件系统时可以使它
/opt 可选文件,如供应商提供的应用程序可以在这里找到
/root 并非根(/)文件系统,它是root用户的主目录
/sbin 系统二进制文件,用于系统管理的可执行文件
/tmp 临时目录,被操作系统或者其它程序用于存储临时文件。用户也可以将文件临时存储在这里。注意的是文件存储在这里可能在任意时间在没有实现通知的情况下被删除
/usr 这些是共享的,只读文件,包括可执行的二进制及库,操作手册文件以及其它类型的文档
/var 可变的数据文件存在在这里,可以包含如日志文件、MySQL和其它数据库文件、网页服务器数据文件、电子收件箱及其它


表1. Linux 文件系统顶级目录

  目录及其子目录展示在表1中,和它们的子目录一起,那些加粗显示的被认为是根文件系统的组成部分。也即是说它们不能作为单独的文件系统创建,在启动时挂载。这是因为它们必须在引导时出现,以便系统正确引导。
  /media 和 /mnt 目录是根目录文件系统的一部分,但是它们不应该包含任何数据。而仅仅是作为临时挂载点。
  其余的目录,即表1中没有背景颜色的目录不需要在引导顺序中出现,而是稍晚点,在为主机执行有用工作做准备的启动时期挂载。
  可以参考官方的Linux 文件系统层次结构标准(FHS)网页去获取这些目录及其子目录的详细信息。维基百科对FHS也有很好的描述。这个标准应当尽可能的遵守,以确保操作和功能的一致性。不管主机上采用哪种文件系统,这个分层目录结构时是相同的。

Linux 统一目录结构

  在一些非Linux的个人计算机操作系统上,如果有多个物理硬盘或者分区,每一个磁盘或者分区会分配一个驱动号。必须知道文件或者程序位于哪个硬盘上,比如C:或者D:。你可以像执行命令一样,发出将驱动号(如D:),去改变当前目录到D:驱动,然后采用cd命令去切换到你期望的文件所在的正确目录。每个驱动都有它自己的分开且完整的目录树。
  Linux文件系统统一所有的物理硬盘和分区到单个目录结构。所有都以(/)根目录作为起点。所有其它目录和它们的子目录都位于Linux根目录下。这意味着Linux上仅有一个目录树可以去搜索文件和程序。
  这是可行的是因为一个文件系统,比如/home,/tmp,/var,/opt或者/usr可以在单独的物理磁盘、分区或者根目录下的逻辑卷上创建,然后挂载到一个挂载点上作为文件系统树的一部分。甚至是可移除的驱动器(如USB或外部USB或者ESATA硬盘)也将会被挂载到根目录文件系统,成为目录树整体的一个部分。
  当从一个Linux发行版本升级到另一个发行版本或者跟换到另一个发行版本,这样做好的理由是很明显的。通常情况下,除了升级任何工具,如升级Fedora dnf-upgrade,明智的做法是,在升级过程中偶尔重新格式化包含有操作系统的硬盘来移除掉这段时间积累的碎片。如果/home是根文件系统的一部分,它也将被重新格式化,然后不得不从备份中恢复。将/home 作为独立的文件系统,安装程序将会知道它是一个单独的文件系统,然后忽略对它的格式化。这个方法也可以运用在存储数据库、邮件收件箱、网站及用户或者系统数据的/var目录。
  这里还有其它原因将这部分目录维护在单独的文件系统。例如,很久以前,我还没有意识到将所有的所需Linux目录都作为(/)根目录的一部分潜在的问题,我在我的主目录填充了大量的大文件。因为我的/home和/tmp 都不是单独的文件系统,而是根目录的子目录,然后整个根目录被填满。没有剩余空间给操作系统去创建临时文件或者扩展已有的数据文件。起初,应用程序启动报没有足够的空间保存文件,随后操作系统开始表现异常。在单用户模式引导下,清除用户主目录下的违规文件才能能变得正常运行。随后采用标准的多文件系统设置重新安装Linux才阻止系统再次崩溃。
  我曾经有一次碰到Linux一直正常运行,但是不允许用户登录图形桌面。我可以在本地通过命令行接口(CLI)或者远程采用SSH登录虚拟控制台。问题在于/tmp 文件系统被填充满了,而一些图形界面所需的文件在登录时无法创建。因为CLI登录不需要在/tmp目录下创建文件,空间不足不会阻止从CLI登录。在这个例子中,/tmp目录是独立的文件系统且这个卷组还有足够的空间,/tmp逻辑卷是其中的一部分。在那台主机上,我简单地给/tmp逻辑卷扩容到我预计临时文件所需的大小,这问题就得到。注意到这并不需要重启,只要/tmp文件系统扩容后,就可以登录桌面。
  另一个状况发生在我是一个大型科技公司工作,当实验室管理员的时候。当我们的一个开发者错误的将一个应用安装到/var下。由于/var文件系统满了,系统存放在/var/log下的日志文件由于缺乏空间无法追加消息,导致应用一直崩溃。然而,系统仍然在运行,因为关键的(/)根和/tmp文件系统没有满。移除有问题的应用并重新在/opt/ 文件系统安装就解决了这个问题。

文件系统类型

  Linux 支持读取大约100个分区类型,仅可以创建和读取其中很少的类型。但是它可以将不同的文件系统挂载到相同根目录下。在这个上下文基础上,我们就文件系统在硬盘分区或者逻辑卷上存储和管理用户数据所需的结构和元数据进行讨论。完整的被Linux fdisk命令识别的文件系统分区类型列表如下,所以你可以感受到Linux高度兼容很多类型的系统。
  支持读取如此多的分区类型的主要目的时为了兼容和至少保证与其它计算机系统的文件系统互操作性。当在Fedora创建一个新的文件系统可选项如下列表:

  • btrfs
  • cramfs
  • ext2
  • ext3
  • ext4
  • fat
  • gfs2
  • hfsplus
  • minix
  • msdos
  • ntfs
  • reiserfs
  • vfat
  • xfs

  其它发行版本支持创建不同的文件系统类型,例如CentOS 6仅支持以上列表中加粗显示文件系统。

挂载

  Linux中“挂载”文件系统这个术语指的是在早期计算中,磁带或者可移除磁盘组需要被物理挂载到一个合适的驱动设备。在被放到驱动上以后,磁盘组上的文件系统
将会由操作系统逻辑挂载以使得它的内容可以被操作系统、应用程序和用户所使用。
  挂载点就是一个目录,像其它目录一样,作为根目录文件系统的一部分。所以,比如用户主目录文件系统被挂载到/home目录上。文件系统可以挂载到任何非根文件系统的挂载点上,但是不太常见。
  Linux根文件系统在引导序列的早期被挂载到/root目录。由其它文件系统则在随后被Linux启动程序挂载,如 SystemV 上的 rc 或者 比较新的Linux发行版本的 systemd。在启动进程期间,文件系统的挂载由/etc/fstab配置文件所管理。一个容易记住的方法是fstab代表“文件系统表”,它包含将要被挂载的文件系统列表及其指定的挂载点和各种文件系统所需的选项信息。
  使用 mount 命令可以将文件系统挂载到一个存在的目录或者挂载点。一般而言,任何目录想成为一个挂载点应该是一个空的且不包含任何文件。Linux不会阻止你在一个文件系统或者非空目录挂载。如果你挂载一个文件系统到一个现有目录或者文件系统,原来的内容将会被隐藏并且只有新挂载的文件系统内容是可见的。

总结

  我希望围绕着文件系统这个术语的一些可能的疑惑已经在这篇文章中被澄清。我花了大量的时间和一个很有帮助的导师去真正地理解并欣赏它的复杂、优雅以及Linux文件系统功能的所有含义。
  如果你有任何问题,请将它们添加到下面的评论中,我会尝试去回答它们。

原文地址:An introduction to Linux filesystems