第十一章:Unix文件系统

FreeBSD的文件系统,即Unix文件系统(UFS),是BSD 4.4附带的文件系统的直接后代。最初的UFS作者之一仍然在开发FreeBSD文件系统,近年来添加了许多漂亮的功能。FreeBSD并不是唯一一个仍然使用4.4 BSD文件系统或其后代的操作系统。一个没有特别吹捧其“改进和高级”文件系统的Unix供应商可能正在运行UFS衍生产品。

UFS作为原始文件系统的地位使其得以在整个FreeBSD中扩展。许多UFS概念是FreeBSD支持其他文件系统(从ZFS到光盘)的基础。即使你不打算使用UFS,你也必须了解UFS的基础知识,才能理解FreeBSD是如何管理文件系统的。

与Unix的其他部分一样,UFS旨在有效地处理最常见的情况,同时可靠地支持异常配置。FreeBSD附带的UFS配置为在相对现代的硬件上尽可能广泛地使用,但如果必须的话,您可以选择为数万亿个小文件或六个1TB文件优化特定的文件系统。

我们今天所说的UFS实际上是UFS版本2,或UFS2。原始UFS无法处理现代磁盘大小。

UFS最适合较小的系统,或无法处理ZFS开销的应用程序。许多人更喜欢UFS作为虚拟机。我在第2章中讨论了选择文件系统。

UFS 部件

UFS由两层组成,一层是Unix File System,另一层是Fast File System(FFS)。

两者共同提供数据存储。

快速文件系统

FFS由超级块、块、片段和索引节点构建。

superblock——超级块,记录文件系统的特征。它包含一个神奇的数字,将文件系统标识为UFS,以及内核用于优化文件写入和读取的文件系统几何信息。UFS文件系统保留了超级块的许多备份副本,以防主块损坏。

blocks ——块,是包含数据的磁盘段。FreeBSD默认为32KB块。FFS将块(blocks)映射到底层磁盘或GEOM提供程序上的特定扇区。每个存储的文件都被分解为32KB的块(chunk),每个块(chunk)都存储在自己的块(block)中。

并非所有文件都是32KB的倍数,因此FFS将剩余文件存储为 fragments ——片段。标准是块大小的八分之一,即4KB。例如,一个39KB的文件将填充一个块和两个片段。其中一个片段只有3KB,所以片段确实浪费了磁盘空间,但它们浪费的空间比到处使用完整块要少得多。

UFS如何使用FFS

UFS将某些FFS块分配为 inodes (index nodes) ——索引节点,以将块和片段映射到文件。索引节点包含每个文件的大小、权限以及包含每个文件在内的块和片段列表。总的来说,索引节点中的数据被称为 metadata ——元数据,或关于数据的数据。

每个文件系统都有一定数量的索引节点,与文件系统大小成正比。现代磁盘的每个分区上可能有数十万个索引节点,足以支持数十万个文件。然而,如果你有大量非常小的文件,你可能需要重建你的文件系统来支持额外的索引节点。使用 df -i 查看文件系统上还有多少可用的索引节点。

理论上,可以在FFS以外的存储层上运行UFS。这就是许多基于日志或基于范围的文件系统的工作原理。然而,在几十年的发展过程中,日志和软更新等UFS功能极大地纠缠了FreeBSD的UFS和FFS,将两者分开已经不再现实,甚至不太可能。

Vnodes

如果你使用的唯一文件系统是UFS,并且你的所有硬盘都是永久连接的,那么索引节点和块就可以完美工作。如今,我们经常在不同的机器甚至不同的操作系统之间交换磁盘。您可能需要读取桌面上的光学介质和闪存盘,服务器甚至可能需要接受为不同操作系统格式化的硬盘。

FreeBSD使用一个存储抽象层——虚拟节点(virtual node)或vnode——在文件系统和内核之间进行中介。你永远不会直接操作vnode,但FreeBSD文档经常引用它们。Vnode是内核和您挂载的任何文件系统之间的转换层。如果你是一个面向对象的程序员,可以把vnode想象成所有存储类都继承的基类。当你将文件写入UFS文件系统时,内核会将数据寻址到一个vnode,而这个vnode又映射到UFS索引节点和FFS块。当您写入FAT32文件系统时,内核会将数据寻址到映射到FAT32文件系统特定部分的vnode。仅在处理UFS文件系统时使用inode,但在处理任何文件系统时都使用vnode。

挂载和卸载文件系统

mount(8) 程序的主要功能是将文件系统附加到主机的文件系统树。虽然FreeBSD在启动时挂载 /etc/fstab 中列出的每个文件系统,但您必须了解 mount(8)的工作原理。如果您以前从未玩过挂载,请将FreeBSD测试机引导到单用户模式(见第4章),然后按照以下步骤操作。

在单用户模式下,FreeBSD以只读方式挂载了根分区。在传统的类Unix系统上,根分区仅包含足够执行基本设置、运行核心服务和查找其余文件系统的系统。其他文件系统未被挂载,因此其内容不可访问。当前的FreeBSD安装程序将所有内容都放在根分区中,因此您将获得基本的操作系统,但任何特殊的文件系统、网络挂载等都是空的。您可能需要挂载其他文件系统来执行系统维护。

挂载标准文件系统

要手动挂载/etc/fstab 中列出的文件系统,如 /var/usr,请为 mount(8) 指定要挂载的文件系统的名称。

这将完全按照 /etc/fstab 中列出的方式挂载分区,并在该文件中指定所有选项。如果你想挂载 /etc/fstab 中列出的所有分区,除了那些标记为 noauto 的分区,请使用 mount-a 标志:

当您挂载所有文件系统时,已挂载的文件系统不会被重新挂载。

特殊挂载

您可能需要在一个不寻常的位置挂载文件系统,或者临时挂载一些东西。当安装新磁盘时,我最常手动挂载磁盘。使用设备节点和所需的装载点。如果我的 /var/db 分区是 /dev/gpt/db ,我想把它挂载到 /mnt 上,我会运行:

卸载分区

当您想断开文件系统与系统的连接时,使用 umount(8) 告诉系统卸载分区。(请注意,该命令是 umount ,而不是 unmount。)

您无法卸载任何程序正在使用的文件系统。如果您无法卸载分区,那么您可能正在以某种方式访问它。即使装载目录中的命令提示符也会阻止您卸载底层分区。运行 fstat|grep /usr (或任何分区)都会暴露阻塞程序。

UFS挂载选项

FreeBSD支持几种改变文件系统行为的挂载选项。手动挂载分区时,可以使用 -o 指定任何挂载选项:

您还可以在 /etc/fstab 中指定挂载选项(请参阅第10章)。在这里,我在/home文件系统上使用 ro 选项,就像在前面的命令行中一样。

mount(8)手册页列出了所有UFS挂载选项,但以下是最常用的选项。

只读挂载

如果你想查看磁盘的内容,但不允许更改它们,请以 read-only——只读方式挂载分区。您无法更改磁盘上的数据或写入任何新数据。在大多数情况下,这是装载磁盘最安全、最无用的方法。

许多系统管理员希望将根分区(甚至可能是 /usr)挂载为只读,以尽量减少入侵者或恶意软件对系统的潜在损害。这最大限度地提高了系统的稳定性,但大大增加了维护的复杂性。如果您使用自动部署系统,如Ansible或Puppet,并且习惯性地从头开始重新部署服务器而不是升级它们,那么只读挂载可能非常适合您。

只读挂载在损坏的计算机上特别有价值。虽然FreeBSD不允许你在损坏或脏的文件系统上执行标准的读写挂载,但如果文件系统不是太糟糕,它将执行只读挂载。这使您有机会从即将耗尽的磁盘中恢复数据。

要以只读方式挂载文件系统,请使用 rdonlyro 选项。两者工作方式相同。

同步挂载

Synchronous (or sync) mounts 是挂载系统的老式方法。当你写入同步挂载的磁盘时,内核会等待写入是否实际完成,然后再通知程序。如果写入未成功完成,程序可以选择相应的操作。

同步挂载在发生崩溃时提供了最大的数据完整性,但会很慢。诚然,“慢”在今天是相对的,即使是廉价的磁盘也比几年前的高端磁盘要好。当你希望在数据完整性方面有强烈需求时,可以考虑使用同步挂载,但在几乎所有情况下,这都是多余的(overkill)。

要同步挂载分区,请使用选项 sync

异步挂载

asynchronous mounts 虽然几乎被软更新取代,但你仍然会听到它们的消息。为了以更高的风险更快地访问数据,可使用异步挂载分区。当磁盘异步挂载时,内核将数据写入磁盘,并在不等待磁盘确认数据确实写入的情况下告诉写入程序写入成功。

异步挂载在一次性文件系统上很好,例如在关闭时消失的内存文件系统,但不要将其用于重要数据。异步挂载和带有软更新的 noasync 之间的性能差异很小。(我将在下一节介绍noasync。)

要异步挂载分区,请使用 async 选项。

结合同步和异步

FreeBSD的默认UFS挂载选项将同步和异步挂载组合为 noasync 。使用noasync,影响索引节点的数据将同步写入磁盘,而实际数据将异步处理。结合软更新(见本章后面),noasync 挂载创建了一个非常健壮的文件系统。

由于noasync挂载是默认设置,您不需要在挂载时指定它,但当其他人这样做时,不要让它混淆您。

禁用Atime

UFS中的每个文件都包含一个访问时间戳,称为 atime,它记录了文件最后一次访问的时间。如果你有大量的文件,不需要这些数据,你可以挂载磁盘 noatime,这样UFS就不会更新这个时间戳。这对于承受重负载的闪存介质或磁盘(如Usenet新闻假脱机驱动器)最有用。不过,有些软件使用atime,所以不要盲目禁用它。

禁用执行

你的政策可能会说某些文件系统不应该有可执行程序。noexec 挂载选项阻止系统执行文件系统上的任何程序。挂载 /home noexec可以帮助防止用户运行自己的程序,但为了使其有效,还可以挂载 /tmp/var/tmp 以及用户可以编写自己的文件noexec的任何其他位置。

noexec挂载不会阻止用户在Perl、Python或其他语言中运行shell脚本或解释脚本。虽然脚本可能位于noexec文件系统上,但解释器通常不在。

noexec挂载的另一个常见用途是,当您的文件系统包含不同操作系统或不同硬件架构的二进制文件,并且您不希望任何人执行它们时。

禁用Suid

Setuid程序允许用户像其他用户一样运行程序。例如,诸如 login(1)之类的程序必须以root身份执行操作,但必须由普通用户运行。Setuid程序显然必须小心编写,这样入侵者就不能利用它们来未经授权地访问您的系统。许多系统管理员习惯性地禁用所有不需要的setuid程序。 nosuid 选项禁用文件系统上所有程序的setuid访问。与noexec一样,脚本包装器可以很容易地避开nosuid限制。

禁用群集

FFS通过集群优化物理介质上的读写。它不是将文件分散在硬盘上,而是将整个文件写入大块。同样,以较大的块读取文件也是有意义的。您可以使用装载选项 noclusterr(用于读取集群)和 noclusterw(用于写入集群)禁用此功能。

禁用Symlinks

nosymfollow 选项禁用文件的符号链接或别名。Symlinks 主要用于为驻留在其他分区上的文件创建别名。要为同一分区上的另一个文件创建别名,请使用常规链接。有关链接的讨论,请参见 ln(1)

目录的别名总是符号链接;你不能使用硬链接。

UFS 弹性

UFS起源于断电意味着数据丢失的时代。经过几十年的使用和调试,UFS几乎从不丢失数据,特别是与其他开源文件系统相比。UFS通过仔细的完整性检查来实现这种弹性,特别是在停电等意外停机后。

弹性的重点不是验证磁盘上的数据——UFS在这方面做得很好。而是为了在意外关闭后加快完整性验证和文件系统恢复。现代磁盘的大小意味着在没有额外弹性的情况下,验证可能需要很长时间。100MB文件系统的完整性检查比多TB文件系统的相同完整性检查快得多!增加弹性可以缩短恢复时间。

UFS提供了几种方法来提高UFS文件系统的弹性,例如软更新和日志记录。在创建文件系统之前,请选择一个符合您需求的文件系统。

软更新

Soft updates ——软更新,是一种用于组织和安排磁盘写入的技术,使文件系统元数据始终保持一致,几乎具有异步挂载的性能和同步挂载的可靠性。这并不意味着所有数据都会安全地写入磁盘——在错误的时刻发生电源故障仍可能丢失数据。无论操作系统如何操作,在电源中断的精确毫秒内写入磁盘的文件都无法到达磁盘。但磁盘上的实际内容在内部是一致的。软更新使UFS能够快速从故障中恢复。

您可以在挂载或创建文件系统时启用和禁用软更新。

随着文件系统的增长,软更新显示出其局限性。多TB文件系统仍然需要相当长的一段时间才能从计划外关闭中恢复。原始的软更新日记纸(http://www.mckusick.com/softdep/suj.pdf)提到一个92%满的14驱动器阵列,其文件系统被故意损坏,需要10个小时进行完整性检查。在那之前,你需要一个日志(journal)。

软更新日志

journaling filesystem —— 日志文件系统记录实际文件系统之外的任何更改。更改会迅速转储到存储中,然后以更悠闲的速度插入文件系统。如果系统意外死亡,文件系统会自动从日志中恢复任何更改。这大大降低了启动时重建文件系统完整性的要求。当你安装FreeBSD时,它默认使用软更新日志创建UFS分区。

软更新日志记录所有元数据更新,而不是记录所有事务,这样文件系统就可以始终恢复到内部一致的状态。基准测试表明,日志只会给软更新增加很小的负载。然而,它确实增加了I/O开销,因为系统必须将所有更改转储到日志中,然后将它们重放到文件系统中。然而,这大大缩短了恢复时间。需要10个小时进行完整性检查的14个驱动器阵列?使用日志从相同的损伤中恢复不到一分钟。

日志的软更新非常强大。你为什么不总用日志?软更新日志会禁用UFS快照。如果您需要UFS快照,则无法用日志。不过,如果你需要快照,最好还是使用ZFS。FreeBSD版本的dump(8)使用UFS快照来备份实时文件系统。只有我们这些熟悉Unix的人不再使用dump,这主要是因为我们已经知道了,但如果你的组织要求使用 dump(8),你需要另一个弹性选项。

GEOM 日志

FreeBSD还可以通过 gjournal(8) 在GEOM级别上发布日志。与任何其他文件系统日志一样,gjournal记录文件系统事务。在启动时,FreeBSD会检查日志文件中是否有任何尚未写入文件系统的更改,并进行这些更改,以确保文件系统的一致性。gjournal早于软更新日志。

虽然软更新只记录元数据,但gjournal记录所有文件系统事务。您不太可能在系统故障中丢失数据,但所有内容都会被写入两次,这会影响性能。不过,如果你在使用gjournal,不要使用任何类型的软更新。您还应该异步挂载文件系统。您可以在gjournaled文件系统上使用快照。

gjournal每个文件系统使用1GB的磁盘。你不能只是打开和关闭它——你必须有空间放日志。您可以为日志使用单独的分区,或者在为日志留出空间的情况下将千兆字节包含在分区中。如果您决定将gjournal添加到现有分区中,则需要在某个地方找到该空间。

你应该使用gjournal还是软更新日记?如果可能的话,我建议使用软更新日志。如果这不是一个选项,请使用纯软更新。如果需要UFS快照,请使用GEOM日志记录,包括快照上的 dump(8) 。就我个人而言,我不再使用gjournal。

创建和调整UFS文件系统

在上一章中,我们对磁盘进行了分区和标记。现在,让我们在这些分区上放置一个文件系统。使用设备节点作为最后一个参数,使用 newfs(8)创建UFS文件系统。在这里,我在设备 /dev/gpt/var 上创建了一个文件系统:

第一行重复设备节点并打印分区的大小➊,以及块➋和片段大小➌。您将获得文件系统几何信息➍,磁盘几何形状与硬件有某种关系的时代的遗迹。最后,newfs(8) 打印一个超级块备份列表➎。文件系统越大,得到的备份超级块就越多。

如果你想使用软更新日志记录,请添加 -j 标志。要使用不记录日志的软更新,请添加 -U 标志。创建文件系统后,您可以使用 tunefs(8) 启用和禁用软更新日志记录和纯软更新。

UFS 标签

设备节点可以更改,但标签保持不变。最佳做法是标记GPT分区,但不能标记MBR分区。MBR上的UFS文件系统可以使用带有-L标志的UFS标签。

标签出现在 /dev/ufs 中。在 /etc/fstab 和其他配置文件中使用它们,以避免磁盘重命名混乱。您不能将UFS标签应用于非UFS文件系统。

如果您在GPT分区上使用UFS,请选择GPT或UFS标签。由于枯萎,你一次只能看到一个标签,可能会让自己感到困惑。

块和碎片大小

UFS的效率与读取或写入的块和片段的数量成正比。一般来说,FreeBSD可以在读取20块文件所需时间的一半内读取10块文件。FreeBSD开发人员选择了默认的块和片段大小,以容纳最广泛的文件。

如果您有一个特殊用途的文件系统,其中绝大多数包含大文件或小文件,您可以在创建文件系统时考虑更改块大小。虽然您可以更改现有文件系统的块大小,但这是一个糟糕的主意。块大小必须是2的幂。片段大小是块大小的八分之一的假设在许多地方都是硬编码的,因此让 newfs(8)根据块大小计算片段大小。

假设我有一个专用于大文件的文件系统,我想增加块大小。默认块大小为32KB,因此下一个较大的块大小为64KB。使用 -b 指定新块大小。

如果你有很多小文件,你可以考虑使用较小的块大小。需要注意的一点是碎片大小小于底层磁盘的物理扇区大小。FreeBSD默认为4KB片段。如果你的磁盘有4KB的扇区,不要使用较小的片段大小。如果你绝对确定你的磁盘有512字节的物理扇区,你可以考虑创建一个16KB(甚至8KB)块大小和相应2KB或1KB片段大小的文件系统。

在我的系统管理员职业生涯中,我只遇到过两次需要自定义块大小。在遇到性能问题之前,不要使用。

使用GEOM日志

在使用 gjournal(8) 之前,请决定将1GB日志放在哪里。如果可能的话,我建议将该GB包含在文件系统分区中。这意味着,如果你想要一个50GB的文件系统,把它放在51GB的分区中。否则,请使用单独的分区。

在执行任何gjournal操作之前,使用 gjournal load 或在/boot/loader.conf 中加载geom_journal 内核模块。

要在日志中包含分区的同时创建gjournal提供程序,请使用 gjournal label 命令。

如果你想让日记账有一个单独的提供者,请将该提供者作为第二个参数添加。

如果成功,这些命令将静默运行。他们创建了一个新的设备节点,其名称与您的日志设备相同,但末尾添加了 .journal 。运行 gjournal label da3p5 会创建 /dev/da3p5.journal 。从现在开始,在日志设备节点上执行所有工作。

在日志设备上创建新的UFS文件系统。使用 -J 标志告诉UFS它正在gjournal的顶部运行。不要启用任何类型的软更新,包括软更新日志。它似乎工作了一段时间。那么就不会了。

异步挂载您的gjournal文件系统。然而,适用于异步挂载的正常警告不适用于gjournal。gjournal GEOM模块处理通常由文件系统管理的验证和完整性检查。

文档中说,您可以将现有分区转换为使用gjournal,前提是您有一个单独的日志分区,并且现有文件系统的最后一个扇区是空的。在实践中,我发现现有文件系统的最后一个扇区总是满的,但如果你想的话,试着阅读 gjournal(8) 了解详细信息。

调整 UFS

您可以使用 tunefs(8) 查看和更改每个UFS文件系统上的设置。这允许您启用和禁用功能;此外,您还可以调整UFS写入文件、管理可用空间和使用文件系统标签的方式。

查看当前设置

使用 -p 标志和分区的当前装载点或底层提供程序查看文件系统的当前设置。

许多可用的设置与我们没有涵盖的特定安全功能有关。像MAC限制➋和所有不同类型的ACL➊这样的主题占据了整本书。但我们可以看到,这个文件系统使用软更新➌和软更新日志➍,尽管它不使用gjournal➎。我们得到了最小的可用空间量➏。最后,我们得到了不存在的UFS标签➐。

使用 tunefs(8) 更改未安装文件系统上的任何这些设置。方便的是,tunefs(8) 显示命令行标志来寻址每个。在更改文件系统的设置之前,我通常会引导到单用户模式。

您可能会注意到,您可以调整各种文件系统内部,如块排列和文件系统几何结构。不要。在FreeBSD使用的二十多年里,我从未见过有人通过摆弄这些旋钮来改善他们的处境。我多次看到人们摆弄这些旋钮,破坏了他们的一天。

但是,让我们看看您可能实际需要启用和禁用的设置。

软更新和日志

使用 -j 标志启用或禁用文件系统上的软更新日志记录。这会自动启用软更新。

要禁用软更新日志记录,请使用 disable 关键字。

非日志文件系统上的软更新日志只会混淆问题。挂载文件系统,并删除文件系统根目录中的 .sujournal 文件。请注意,关闭日记会使软更新仍然存在。使用 -n enable-n disable 打开和关闭软更新(无日志记录)。

最小可用空间

UFS保留了每个分区的8%,以便有空间重新排列文件以获得更好的性能。我在第249页的“UFS空间预订”中进一步讨论了这一点。如果要更改此百分比,请使用 -m 标志。在这里,我告诉文件系统只保留5%的磁盘。

您现在应该有更多可用的磁盘空间。此外,UFS将运行得更慢,因为它总是尽可能紧密地打包文件系统。

SSD TRIM

固态磁盘使用磨损均衡(wear-leveling)来延长其使用寿命。如果文件系统在每个块不再使用时通知SSD,磨损均衡效果最佳。TRIM协议处理此通知。使用 -t 标志在SSD支持的文件系统上启用TRIM支持。

为了获得最佳效果,请为固态驱动器上的每个分区启用TRIM。使用 newfs -E 在文件系统创建时启用TRIM。

标记UFS文件系统

您可以使用 -L 标志将UFS标签应用于现有的文件系统。

不要混淆UFS和GPT标签——你只会把自己弄糊涂。

扩展UFS文件系统

您的虚拟机空间不足?将磁盘放大,并扩展最后一个分区以覆盖该空间,如第10章所述。但是那个分区上的文件系统呢?这就是 grows(8) 发挥作用的地方。

growfs(8)命令扩展现有的UFS文件系统以填充它所在的分区。给growfs一个参数,即文件系统的设备节点。如果你喜欢,可以使用标签。

grows(8) 请求确认时➊,你必须输入完整的单词 yes。任何其他答案,包括像许多其他程序接受的简单 y ,都会取消操作。确认操作后,growfs(8) 将根据需要添加额外的块、超级块和索引节点来填充分区。

如果你不想让文件系统填满整个分区,你可以用 -s指定一个大小。在这里,我将这个分区扩展到80GB。

我强烈建议您将文件系统设置为与底层分区相同的大小,除非您想让同事打你耳光。

UFS 快照

您可以随时拍摄UFS文件系统的映像;这被称为 snapshot——快照。您可以对文件系统进行快照,擦除和更改一些文件,然后从快照中复制未更改的文件。像 dump(8) 这样的工具使用快照来确保一致的备份。UFS快照不如ZFS快照强大或灵活,但在其限制范围内,它们是一种可靠的工具。

UFS快照需要软更新,但与软更新日志不兼容。每个文件系统最多可以有20个快照。

快照允许您获取已编辑或删除文件的旧版本。通过将文件作为内存设备挂载来访问快照的内容。我将在第13章讨论存储设备。

拍摄和销毁快照

使用 mksnap_ffs(8)创建快照。此程序假设您想对当前工作目录所在的文件系统进行快照。请将快照位置作为参数。快照传统上位于文件系统根目录的 .snap 目录中。如果您使用的是自动创建和删除快照的工具,如 dump(8),请检查那里的快照文件。不过,如果你不喜欢这个位置,你可以把它们放在你要拍摄快照的文件系统上的任何地方。在这里,我拍摄了 /home 文件系统的快照:

快照使用磁盘空间。您无法拍摄完整文件系统的快照。

快照只是一个文件。删除该文件,即可销毁快照。

查找快照

快照是文件,您可以将它们放在文件系统上的任何位置。这意味着很容易失去它们。使用 find(1)-flags snapshot选项查找文件系统上的所有快照。

这是我的杂散快照!

快照磁盘使用情况

快照记录当前文件系统和拍摄快照时存在的文件系统之间的差异。拍摄快照后,每次文件系统更改都会增加快照的大小。如果删除文件,快照将保留该文件的副本,以便以后恢复。

这意味着从带有快照的文件系统中删除数据实际上并没有释放空间。如果您有 /home 分区的快照,并且删除了一个文件,则删除的文件将添加到快照中。

确保带有快照的文件系统始终有足够的可用空间。如果您尝试拍摄快照,但 mksnap_ffs(8) 抱怨由于没有空间而无法拍摄,则您可能已经拥有该文件系统的20个快照。

UFS恢复和维修

从硬件故障到系统管理不当,一切都可能损坏您的文件系统。UFS的所有弹性技术都旨在快速恢复数据完整性,但没有什么能完全保证完整性。

让我们讨论一下FreeBSD如何保持每个UFS文件系统的整洁。

系统关闭:同步器

当你关闭FreeBSD系统时,内核会将其所有数据同步到硬盘驱动器,标记磁盘干净,然后关闭。这是由一个名为 syncer 的内核进程完成的。在系统关闭期间,同步器会报告其同步硬盘的进度。

在关机期间,您会从同步器中看到奇怪的东西。syncer遍历需要同步到磁盘的vnode列表,使其能够支持所有文件系统,而不仅仅是UFS。由于软更新,将一个vnode写入磁盘可能会生成另一个需要更新的脏vnode。您可以看到写入磁盘的缓冲区数量从高值迅速下降到低值,并可能在零和低值之间反弹一到两次,因为系统真正地同步了硬盘驱动器。

如果同步器没有机会完成,或者由于你笨拙的摸索,同步器根本无法运行,那么你会得到一个脏文件系统。

脏文件系统

不,磁盘在使用过程中不会变脏(尽管磁盘上的灰尘会很快损坏它,加水也无济于事)。一个肮脏的UFS分区处于一种不确定状态;操作系统已要求将信息写入磁盘,但数据尚未完全存储在物理介质上。部分数据块可能已被写入,索引节点可能已被编辑,但数据未被写出,或两者的任何组合。实时文件系统几乎总是脏的。

如果具有脏文件系统的主机发生故障,例如,由于死机或Bert绊倒电源线,当系统再次启动时,文件系统仍然是脏的。内核拒绝挂载脏文件系统。

清理文件系统可以恢复数据完整性,但并不一定意味着所有数据都在磁盘上。如果系统死机时文件被半写到磁盘,则文件将丢失。没有什么可以恢复丢失的文件的一半,而磁盘上的那一半基本上是无用的。

当FreeBSD尝试挂载日志文件系统时,日志文件系统应该会自动恢复。如果文件系统无法恢复,或者您没有日志,则需要使用传奇的 fsck(8)

文件系统检查 : fsck(8)

fsck(8)程序检查UFS文件系统,并尝试验证每个文件是否都附加到正确的索引节点和正确的目录中。这就像验证数据库的引用完整性。如果文件系统仅受到轻微损坏,fsck(8) 可以自动恢复完整性并使文件系统重新投入使用。

修复损坏的文件系统需要时间和内存。运行 fsck(8)需要大约700MB的RAM来分析1TB的文件系统。大多数计算机系统都有相当比例的内存和存储系统:很少有主机有512MB的RAM和PB的磁盘。但是你应该知道,有可能创建一个如此大的UFS文件系统,以至于系统没有足够的内存来修复它。

手动fscks运行

重启时这种自动 fsck 偶尔会失败。当您检查控制台时,您将看到一个单用户模式提示和一个手动运行 fsck(8)的请求。

首先用 fsck -p整理(preening)文件系统。这会自动纠正一堆不太严重的错误,而无需征得您的批准。预处理很少会导致数据丢失。这通常是成功的,但如果它不起作用,它会要求你运行一个“完整的fsck”

如果在命令提示符下输入 fsckfsck(8) 将验证磁盘上的每个块和索引节点。它会找到任何与索引节点解除关联的块,并猜测它们如何组合在一起以及应该如何附加。但是,fsck(8)可能无法识别这些文件属于哪个目录。

然后,fsck(8) 询问您是否要执行这些重新连接。如果你回答 n,它会删除损坏的文件。如果你回答 y ,它会将丢失的文件添加到分区根目录中的 lost+found 目录中,并用一个数字作为文件名。例如,/usr 分区上的 lost+found 目录是 /usr/lost+found 。如果只有几个文件,您可以手动识别它们;如果你有很多文件,并且正在寻找特定的文件,那么 file(1)grep(1)等工具可以帮助你通过内容识别它们。

如果你回答 n,这些未知数据块仍然与文件系统分离。在您通过其他方式修复它们之前,文件系统仍然是脏的。

信任 fsck(8)

如果 fsck(8)无法找出文件的位置。你能?如果没有,您真的别无选择,只能信任 fsck(8)来恢复您的系统或从备份中还原。

完整的 fsck(8)运行检查每个块、inode和超级块,并识别每个不一致。它要求您键入 yn 来批准或拒绝每一项更正。你拒绝的任何改变都必须通过其他方式来修复自己。你可能会在控制台上花几个小时键入y,y,y

所以我再问一遍:如果 fsck(8)不能解决问题,你能吗?

如果不能,请考虑 fsck -y-y 标志告诉 fsck(8)在不提示的情况下尽可能地重新组装这些文件。它假设你对它的所有问题都回答“yes”,即使是真正危险的问题。使用 -y 会自动触发 -R ,这将告诉 fsck(8) 重试清理每个文件系统,直到它成功或连续10次失败为止。要么治愈,要么杀死。你有备份,对吧?

危险! 运行 fsck -y 不能保证安全。有时,在运行-current或执行其他愚蠢的事情时,我会让 fsck -y 将文件系统的全部内容迁移到 lost+found。此时恢复变得困难。话虽如此,在一个运行FreeBSD稳定版和标准UFS文件系统的生产系统中,我从来没有遇到过问题。

您可以将系统设置为在启动时自动尝试 fsck -y。然而,我不建议这样做,因为如果我的文件系统有一点可能陷入数字涅盘,我想知道这件事。我想自己键入有问题的命令,并感受到听到磁盘抖动的恐惧。此外,发现你的系统被破坏而不知道它是怎么变成这样的,总是令人不快。如果你比我勇敢,在 rc.conf 中设置 fsck_y_enable=“YES”

避免 fsck -y

如果你不想使用 fsck -y ,你有什么选择?好吧,fsdb(8)clri(8)允许您调试文件系统并将文件重定向到正确的位置。您可以将文件还原到其正确的目录和名称。然而,这很难做到,而且只建议用于Secret Ninja Filesystem Masters。

后台 fsck

后台(background) fsck 为UFS提供了日志文件系统的一些好处,而实际上不需要日志记录。您必须使用没有日志记录的软更新才能使用后台fsck。(使用日志记录的软更新比后台fsck要好得多。)当FreeBSD在重启后看到后台fsck正在处理时,它会以读写方式挂载脏磁盘。当服务器运行时,fsck(8) 在后台运行,识别松散的文件并在幕后整理它们。

后台 fsck实际上有两个主要阶段。当FreeBSD在初始引导过程中发现脏磁盘时,它会对磁盘进行初步的 fsck(8)评估。fsck(8) 程序决定在系统运行时是否可以修复损坏,或者是否需要完全的单用户模式fsck运行。最常见的情况是,fsck认为它可以继续并让系统启动。系统达到单用户模式后,后台fsck以低优先级运行,逐一检查分区。fsck过程的结果显示在 /var/log/messages 中。

您可以预期,在后台fsck期间,任何需要磁盘活动的应用程序的性能都很糟糕。fsck(8)程序占据了磁盘可能活动的很大一部分。虽然你的系统可能很慢,但它至少会启动。

在后台fsck之后,您 必须 检查 /var/log/messages 是否有错误。初步的fsck评估可能会出错,也许确实需要分区上的完整单用户模式的fsck。如果您发现这样的消息,请在几个小时内安排停机时间以纠正问题。虽然不方便,但让系统在预定时间内停机比停电和由此导致的单用户模式的 fsck -y 造成的计划外停机要好。

强制在脏磁盘上进行读写装载

如果你真的想强制FreeBSD在不使用后台fsck的情况下以读写方式挂载脏磁盘,你可以这样做。你不会喜欢结果的。完全。但是,正如 mount(8) 所述,一些读者会认为这是一个好主意,除非他们知道为什么。使用 -w(读写)和 -f (强制)标志进行 mount(8)

以读写方式挂载脏分区会损坏数据。请注意,这个句子中没有像 maycould 这样的词。还要注意,我没有使用 recoverable。安装脏文件系统可能会使您的计算机死机。它可能会破坏分区上所有剩余的数据,甚至破坏底层文件系统。强制对脏文件系统进行读写挂载是非常糟糕的juju。不要这样做。

后台 fsck, fsck -y, 前台 fsck, Oy Vey!

所有这些不同的 fsck(8) 问题和情况都可能发生,但FreeBSD什么时候使用每个命令?FreeBSD使用以下条件来决定何时以及如何在文件系统上执行 fsck(8)

配置UFS文件系统时,请考虑恢复路径。

UFS空间预订

UFS文件系统永远不会像你想象的那么大。UFS保留了8%的文件系统空间用于动态优化。只有root可以写超过这个限制。这就是为什么文件系统似乎可以使用超过100%的可用空间。为什么是8%?这个数字是多年经验和现实世界测试的结果。对于一般的文件系统来说,8%的延迟并不是什么大问题,但随着文件系统的增长,这可能是相当大的。在1PB磁盘阵列上,UFS保留了80TB的容量。

UFS的行为因文件系统的满度而异。在空文件系统上,它会优化速度。一旦文件系统达到92%满(占总大小的85%,包括8%的保留),它就会切换以优化空间利用率。大多数人都会做同样的事情——一旦你把洗衣篮装满,你就可以塞进更多脏衣服,但这需要更多的时间和精力。UFS对文件进行碎片化,以更有效地利用空间。碎片会降低磁盘性能。随着可用空间的缩小,UFS越来越努力地提高空间利用率。一个满的UFS文件系统的运行速度大约是正常速度的三分之一。

您可能希望使用 tunefs(8)来减少FreeBSD保留的磁盘空间量。它不会像你想象的那么有帮助。将保留空间减少到5%或更少告诉UFS始终使用空间优化,并尽可能紧凑地打包文件系统。

增加保留空间百分比并不能提高性能。如果增加保留空间百分比,使文件系统显示为已满,则普通用户将无法写入文件。

保留的空间可能会混淆NFS等工具。其他一些可以通过NFS挂载UFS的操作系统会看到文件系统已满100%,并告诉用户他们无法写入文件,尽管本地客户端可以写入文件。故障排除时请记住这一点。

最好的办法是防止分区填满。

分区有多满?

要了解每个UFS分区还剩多少空间,请使用 df(1)。这列出了系统上的分区、每个分区使用的空间量以及挂载的位置。(不要将 df(1) 与ZFS一起使用;我们将在下一章讨论原因。)

$BLOCKSIZE FreeBSD的磁盘实用程序(包括 df(1))的一个令人讨厌的地方是,它们默认以512字节块的形式提供信息。块对于使用512字节物理块的小型磁盘来说是很好的,但这在今天不是一个有用的衡量标准。环境变量 $BLOCKSIZE 控制 df(1)提供输出的单位。默认的 .cshrc.profile$BLOCKSIZE设置为1KB,这使得 df(1) 显示千字节而不是块。

-h-H 标志告诉 df(1)生成人类可读的输出,而不是使用块。小 -h 使用基数2创建1024字节的兆字节,而大 -H 使用基数10创建1000字节的兆比特。通常,网络管理员和磁盘制造商使用基数10,而系统管理员使用基数2。只要你知道你选择了哪一个,这两种方法都有效。我是一名网络管理员,所以在这些例子中,你会因为我的偏见而受苦,不管我的技术编辑怎么想。

第一行显示了提供程序名称的列标题➊、分区大小、已使用的空间量、可用空间量、已使用空间百分比和装载点。我们可以看到,标记为 /dev/gpt/root ➋的分区大小只有1GB,但只有171MB,剩下785MB可用。它已满18%,安装在/上。

如果你的系统和我的一样,磁盘使用率就会无缘无故地持续增长。请查看此处的 /usr 分区➌。已满98%。您可以使用ls-l标识单个大文件,但在系统中的每个目录上递归执行此操作是不切实际的。

du(1)程序显示单个目录中的磁盘使用情况。它的初始输出令人望而生畏,可能会吓跑没有经验的用户。在这里,我们使用 du(1)来找出是什么占用了我主目录中的所有空间:

这会一直持续下去,显示每个子目录并以块为单位给出其大小。给出了每个子目录的总数——例如,$HOME/bin 的内容总计53336个块,约53MB。我可以坐下来让 du(1)列出每个目录和子目录,但这样我就必须挖掘出比我真正想挖掘的更多的信息。而且块并不是一种方便的测量方法,尤其是当它们被左对齐打印时。

让我们清理一下。首先,du(1)支持一个 -h 标志,就像 df 一样。此外,我不需要看到每个子目录的递归内容。我们可以使用du的 -d标志控制显示的目录数量。此标志接受一个参数,即要显式列出的目录数量。例如,-d0 深入一个目录,并给出一个目录中文件的简单小计。

我的主目录中有14 GB的数据?让我们深入一层,找出最大的子目录。

显然,我必须在其他地方寻找存储空间,因为我的主目录中的数据太重要了,无法删除。也许我应该在这个主机下扩展虚拟磁盘。

如果您不太依赖 -h 标志,则可以使用sort(1)通过类似 du -kxd 1 | sort -n 的命令查找最大的目录。

添加新的UFS存储

无论你做了多少计划,最终你的硬盘都会被填满。您需要添加磁盘。在使用新硬盘之前,您必须对驱动器进行分区,创建文件系统,挂载这些文件系统,并将数据移动到其中。

像最初安装时一样,仔细考虑新磁盘分区和文件系统的设计。在安装时正确分区磁盘比返回并重新分区磁盘上的数据要容易得多。

备份,备份,备份! 在使用磁盘之前,请确保您有完整的备份。一个愚蠢的胖手指错误可以摧毁你的系统!虽然您很少计划重新格式化根文件系统,但如果发生这种情况,您希望非常非常快速地恢复。

磁盘分区

虽然您可以以任何方式对磁盘进行分区,但我建议新磁盘使用与主机其余部分相同的分区方案。一个磁盘用MBR分区,另一个用GPT分区,这很烦人。在这个例子中,我将使用GPT。

决定如何分割磁盘。这是一个1TB的磁盘。100GB将用于扩展的 /tmp 。我将为我的新数据库分区分配500GB。剩余的空间被分隔开,但被标记为 emergency 。我不会在那个空间中放置文件系统;它就在那里,以防我需要进行完整的内存转储或必须将一些文件放在某个地方。我把它放在数据库分区旁边,这样我就可以在需要时扩展数据库分区。我可以不划分紧急空间,但我希望它有一个GPT标签,这样我的其他系统管理员就会意识到这个空闲空间不是偶然的。

首先销毁磁盘上的任何分区方案并创建GPT方案。

现在创建100GB /tmp 和500GB数据分区,并将其余部分转储到紧急分区中。

通过 gpart show 检查你的作品。

在每个分区上创建文件系统。

由于 /tmp 在每次启动时都会被清空,我不希望在 /tmp 上使用软更新日志记录。相反,我会挂载 /tmp async并在启动时运行 newfs /dev/gpt/tmp 。很多时候,newfs(8)rm(1)快。

配置 /etc/fstab

现在告诉 /etc/fstab 您的文件系统。我们在第10章讨论 /etc/fstab 的格式。

FreeBSD将在启动时识别文件系统,或者您可以在命令行挂载这些新分区。不过,暂时不要重新启动或挂载分区。首先,您需要将文件移动到这些文件系统。

将现有文件安装到新磁盘上

您可能打算用新磁盘替换或细分现有分区。您需要将新分区挂载到临时挂载点,将文件移动到新磁盘,然后在所需位置重新挂载分区。虽然 /tmp 没有任何文件,但如果我们要安装一个新的数据库文件系统,我们可能有数据库文件要放在那里。

在移动文件之前,请关闭使用它们的任何进程。在复制文件时,您无法成功复制正在更改的文件。如果要移动数据库文件,请关闭数据库。如果你要移动邮件卷轴,请关闭所有邮件程序。这是我建议在单用户模式下进行所有新磁盘安装的重要原因。

现在将新分区装载到临时装载点上。这正是 /mnt 的用途。

现在,您必须将文件从其当前位置移动到新磁盘,而不更改其权限。这对于 tar(1)来说相当简单。您可以简单地将现有数据映射到磁带或文件,并在新位置解压缩,但这有点笨拙。将一个 tar 倒入另一个tar 中,以避免中间步骤。

如果你在聚会上不说Unix,这看起来相当令人震惊。让我们拆除它。首先,我们去旧目录,把所有东西打包。然后,将输出传输到第二个命令,该命令提取新目录中的备份。此命令完成后,您的文件将安装在新磁盘上。例如,要将 /usr/local/etc/postgres 移动到临时挂载在 /mnt 的新分区上,您需要执行以下操作:

检查临时装载点,以确保您的文件确实在那里。一旦您确信文件已正确移动,请从旧目录中删除文件,并将磁盘装载到新位置。例如,从 /usr/local/etc/postgres 复制文件后,您可以运行:

您现在可以恢复正常操作。我建议重新启动以验证一切是否完全按照您的预期恢复。

可堆叠Mounts

也许你不在乎你的旧数据;您希望拆分现有的文件系统只是为了获得更多空间,并且您打算从备份中恢复数据。那很好。所有FreeBSD文件系统都是可堆叠的。这是一个先进的想法,在日常系统管理中并不是非常有用,但当你试图将一个分区一分为二时,它可能会让你感到痛苦。

例如,假设您在 /usr/src 中有数据。查看磁盘上使用了多少空间,然后在 /usr/src 上挂载一个新的空分区。如果你稍后查看目录,你会看到它是空的。

问题是:旧文件系统上仍然有它所有的原始数据。新文件系统被挂载在旧文件系统的“上方”,所以你只能看到新文件系统。旧文件系统没有比移动数据之前更多的可用空间。如果你卸载新的文件系统并再次检查目录,你会看到数据奇迹般地恢复了!新的文件系统掩盖了较低的文件系统。

虽然您看不到数据,但旧文件系统上的数据仍然会占用空间。如果您添加文件系统以获得空间,并在旧文件系统的一部分上挂载新文件系统,则不会释放原始文件系统上的任何空间。这个故事的寓意是:即使你正在从备份中恢复数据,也要确保从原始磁盘中删除这些数据以恢复磁盘空间。

现在你可以谈论UFS了,让我们来探索ZFS。