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由两层组成,一层是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块分配为 inodes (index nodes) ——索引节点,以将块和片段映射到文件。索引节点包含每个文件的大小、权限以及包含每个文件在内的块和片段列表。总的来说,索引节点中的数据被称为 metadata ——元数据,或关于数据的数据。
每个文件系统都有一定数量的索引节点,与文件系统大小成正比。现代磁盘的每个分区上可能有数十万个索引节点,足以支持数十万个文件。然而,如果你有大量非常小的文件,你可能需要重建你的文件系统来支持额外的索引节点。使用 df -i
查看文件系统上还有多少可用的索引节点。
理论上,可以在FFS以外的存储层上运行UFS。这就是许多基于日志或基于范围的文件系统的工作原理。然而,在几十年的发展过程中,日志和软更新等UFS功能极大地纠缠了FreeBSD的UFS和FFS,将两者分开已经不再现实,甚至不太可能。
如果你使用的唯一文件系统是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)
指定要挂载的文件系统的名称。
xxxxxxxxxx
# mount /media
这将完全按照 /etc/fstab 中列出的方式挂载分区,并在该文件中指定所有选项。如果你想挂载 /etc/fstab 中列出的所有分区,除了那些标记为 noauto
的分区,请使用 mount
的 -a
标志:
xxxxxxxxxx
# mount -a
当您挂载所有文件系统时,已挂载的文件系统不会被重新挂载。
您可能需要在一个不寻常的位置挂载文件系统,或者临时挂载一些东西。当安装新磁盘时,我最常手动挂载磁盘。使用设备节点和所需的装载点。如果我的 /var/db 分区是 /dev/gpt/db ,我想把它挂载到 /mnt 上,我会运行:
xxxxxxxxxx
# mount /dev/gpt/db /mnt
当您想断开文件系统与系统的连接时,使用 umount(8)
告诉系统卸载分区。(请注意,该命令是 umount
,而不是 unmount
。)
xxxxxxxxxx
# umount /usr
您无法卸载任何程序正在使用的文件系统。如果您无法卸载分区,那么您可能正在以某种方式访问它。即使装载目录中的命令提示符也会阻止您卸载底层分区。运行 fstat|grep /usr
(或任何分区)都会暴露阻塞程序。
FreeBSD支持几种改变文件系统行为的挂载选项。手动挂载分区时,可以使用 -o
指定任何挂载选项:
xxxxxxxxxx
# mount -o ro /dev/gpt/home /home
您还可以在 /etc/fstab 中指定挂载选项(请参阅第10章)。在这里,我在/home文件系统上使用 ro
选项,就像在前面的命令行中一样。
xxxxxxxxxx
/dev/gpt/home /home ufs ro 2 2
mount(8)
手册页列出了所有UFS挂载选项,但以下是最常用的选项。
如果你想查看磁盘的内容,但不允许更改它们,请以 read-only——只读方式挂载分区。您无法更改磁盘上的数据或写入任何新数据。在大多数情况下,这是装载磁盘最安全、最无用的方法。
许多系统管理员希望将根分区(甚至可能是 /usr)挂载为只读,以尽量减少入侵者或恶意软件对系统的潜在损害。这最大限度地提高了系统的稳定性,但大大增加了维护的复杂性。如果您使用自动部署系统,如Ansible或Puppet,并且习惯性地从头开始重新部署服务器而不是升级它们,那么只读挂载可能非常适合您。
只读挂载在损坏的计算机上特别有价值。虽然FreeBSD不允许你在损坏或脏的文件系统上执行标准的读写挂载,但如果文件系统不是太糟糕,它将执行只读挂载。这使您有机会从即将耗尽的磁盘中恢复数据。
要以只读方式挂载文件系统,请使用 rdonly
或 ro
选项。两者工作方式相同。
Synchronous (or sync) mounts 是挂载系统的老式方法。当你写入同步挂载的磁盘时,内核会等待写入是否实际完成,然后再通知程序。如果写入未成功完成,程序可以选择相应的操作。
同步挂载在发生崩溃时提供了最大的数据完整性,但会很慢。诚然,“慢”在今天是相对的,即使是廉价的磁盘也比几年前的高端磁盘要好。当你希望在数据完整性方面有强烈需求时,可以考虑使用同步挂载,但在几乎所有情况下,这都是多余的(overkill)。
要同步挂载分区,请使用选项 sync
。
asynchronous mounts 虽然几乎被软更新取代,但你仍然会听到它们的消息。为了以更高的风险更快地访问数据,可使用异步挂载分区。当磁盘异步挂载时,内核将数据写入磁盘,并在不等待磁盘确认数据确实写入的情况下告诉写入程序写入成功。
异步挂载在一次性文件系统上很好,例如在关闭时消失的内存文件系统,但不要将其用于重要数据。异步挂载和带有软更新的 noasync
之间的性能差异很小。(我将在下一节介绍noasync。)
要异步挂载分区,请使用 async
选项。
FreeBSD的默认UFS挂载选项将同步和异步挂载组合为 noasync 。使用noasync,影响索引节点的数据将同步写入磁盘,而实际数据将异步处理。结合软更新(见本章后面),noasync 挂载创建了一个非常健壮的文件系统。
由于noasync挂载是默认设置,您不需要在挂载时指定它,但当其他人这样做时,不要让它混淆您。
UFS中的每个文件都包含一个访问时间戳,称为 atime,它记录了文件最后一次访问的时间。如果你有大量的文件,不需要这些数据,你可以挂载磁盘 noatime,这样UFS就不会更新这个时间戳。这对于承受重负载的闪存介质或磁盘(如Usenet新闻假脱机驱动器)最有用。不过,有些软件使用atime,所以不要盲目禁用它。
你的政策可能会说某些文件系统不应该有可执行程序。noexec
挂载选项阻止系统执行文件系统上的任何程序。挂载 /home noexec可以帮助防止用户运行自己的程序,但为了使其有效,还可以挂载 /tmp 、/var/tmp 以及用户可以编写自己的文件noexec的任何其他位置。
noexec挂载不会阻止用户在Perl、Python或其他语言中运行shell脚本或解释脚本。虽然脚本可能位于noexec文件系统上,但解释器通常不在。
noexec挂载的另一个常见用途是,当您的文件系统包含不同操作系统或不同硬件架构的二进制文件,并且您不希望任何人执行它们时。
Setuid程序允许用户像其他用户一样运行程序。例如,诸如 login(1)
之类的程序必须以root身份执行操作,但必须由普通用户运行。Setuid程序显然必须小心编写,这样入侵者就不能利用它们来未经授权地访问您的系统。许多系统管理员习惯性地禁用所有不需要的setuid程序。
nosuid
选项禁用文件系统上所有程序的setuid访问。与noexec一样,脚本包装器可以很容易地避开nosuid限制。
FFS通过集群优化物理介质上的读写。它不是将文件分散在硬盘上,而是将整个文件写入大块。同样,以较大的块读取文件也是有意义的。您可以使用装载选项 noclusterr
(用于读取集群)和 noclusterw
(用于写入集群)禁用此功能。
nosymfollow
选项禁用文件的符号链接或别名。Symlinks 主要用于为驻留在其他分区上的文件创建别名。要为同一分区上的另一个文件创建别名,请使用常规链接。有关链接的讨论,请参见 ln(1)
。
目录的别名总是符号链接;你不能使用硬链接。
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)
,你需要另一个弹性选项。
FreeBSD还可以通过 gjournal(8)
在GEOM级别上发布日志。与任何其他文件系统日志一样,gjournal记录文件系统事务。在启动时,FreeBSD会检查日志文件中是否有任何尚未写入文件系统的更改,并进行这些更改,以确保文件系统的一致性。gjournal早于软更新日志。
虽然软更新只记录元数据,但gjournal记录所有文件系统事务。您不太可能在系统故障中丢失数据,但所有内容都会被写入两次,这会影响性能。不过,如果你在使用gjournal,不要使用任何类型的软更新。您还应该异步挂载文件系统。您可以在gjournaled文件系统上使用快照。
gjournal每个文件系统使用1GB的磁盘。你不能只是打开和关闭它——你必须有空间放日志。您可以为日志使用单独的分区,或者在为日志留出空间的情况下将千兆字节包含在分区中。如果您决定将gjournal添加到现有分区中,则需要在某个地方找到该空间。
你应该使用gjournal还是软更新日记?如果可能的话,我建议使用软更新日志。如果这不是一个选项,请使用纯软更新。如果需要UFS快照,请使用GEOM日志记录,包括快照上的 dump(8)
。就我个人而言,我不再使用gjournal。
在上一章中,我们对磁盘进行了分区和标记。现在,让我们在这些分区上放置一个文件系统。使用设备节点作为最后一个参数,使用 newfs(8)
创建UFS文件系统。在这里,我在设备 /dev/gpt/var 上创建了一个文件系统:
xxxxxxxxxx
# newfs /dev/gpt/var
/dev/gpt/var: ➊51200.0MB (104857600 sectors) ➋block size 32768, ➌fragment size 4096
using ➍82 cylinder groups of 626.09MB, 20035 blks, 80256 inodes.
super-block backups (for fsck_ffs -b #) at:
➎192, 1282432, 2564672, 3846912, 5129152, 6411392, 7693632, 8975872,
--snip--
第一行重复设备节点并打印分区的大小➊,以及块➋和片段大小➌。您将获得文件系统几何信息➍,磁盘几何形状与硬件有某种关系的时代的遗迹。最后,newfs(8)
打印一个超级块备份列表➎。文件系统越大,得到的备份超级块就越多。
如果你想使用软更新日志记录,请添加 -j
标志。要使用不记录日志的软更新,请添加 -U
标志。创建文件系统后,您可以使用 tunefs(8)
启用和禁用软更新日志记录和纯软更新。
设备节点可以更改,但标签保持不变。最佳做法是标记GPT分区,但不能标记MBR分区。MBR上的UFS文件系统可以使用带有-L标志的UFS标签。
xxxxxxxxxx
# newfs -L var /dev/ada3s1d
标签出现在 /dev/ufs 中。在 /etc/fstab 和其他配置文件中使用它们,以避免磁盘重命名混乱。您不能将UFS标签应用于非UFS文件系统。
如果您在GPT分区上使用UFS,请选择GPT或UFS标签。由于枯萎,你一次只能看到一个标签,可能会让自己感到困惑。
UFS的效率与读取或写入的块和片段的数量成正比。一般来说,FreeBSD可以在读取20块文件所需时间的一半内读取10块文件。FreeBSD开发人员选择了默认的块和片段大小,以容纳最广泛的文件。
如果您有一个特殊用途的文件系统,其中绝大多数包含大文件或小文件,您可以在创建文件系统时考虑更改块大小。虽然您可以更改现有文件系统的块大小,但这是一个糟糕的主意。块大小必须是2的幂。片段大小是块大小的八分之一的假设在许多地方都是硬编码的,因此让 newfs(8)
根据块大小计算片段大小。
假设我有一个专用于大文件的文件系统,我想增加块大小。默认块大小为32KB,因此下一个较大的块大小为64KB。使用 -b
指定新块大小。
xxxxxxxxxx
# newfs -b 64K -L home /dev/da0s1d
如果你有很多小文件,你可以考虑使用较小的块大小。需要注意的一点是碎片大小小于底层磁盘的物理扇区大小。FreeBSD默认为4KB片段。如果你的磁盘有4KB的扇区,不要使用较小的片段大小。如果你绝对确定你的磁盘有512字节的物理扇区,你可以考虑创建一个16KB(甚至8KB)块大小和相应2KB或1KB片段大小的文件系统。
在我的系统管理员职业生涯中,我只遇到过两次需要自定义块大小。在遇到性能问题之前,不要使用。
在使用 gjournal(8)
之前,请决定将1GB日志放在哪里。如果可能的话,我建议将该GB包含在文件系统分区中。这意味着,如果你想要一个50GB的文件系统,把它放在51GB的分区中。否则,请使用单独的分区。
在执行任何gjournal操作之前,使用 gjournal load
或在/boot/loader.conf 中加载geom_journal
内核模块。
要在日志中包含分区的同时创建gjournal提供程序,请使用 gjournal label
命令。
xxxxxxxxxx
# gjournal label da3p5
如果你想让日记账有一个单独的提供者,请将该提供者作为第二个参数添加。
xxxxxxxxxx
# gjournal label da3p5 da3p7
如果成功,这些命令将静默运行。他们创建了一个新的设备节点,其名称与您的日志设备相同,但末尾添加了 .journal 。运行 gjournal label da3p5
会创建 /dev/da3p5.journal 。从现在开始,在日志设备节点上执行所有工作。
在日志设备上创建新的UFS文件系统。使用 -J
标志告诉UFS它正在gjournal的顶部运行。不要启用任何类型的软更新,包括软更新日志。它似乎工作了一段时间。那么就不会了。
异步挂载您的gjournal文件系统。然而,适用于异步挂载的正常警告不适用于gjournal。gjournal GEOM模块处理通常由文件系统管理的验证和完整性检查。
xxxxxxxxxx
/dev/da3p5.journal /var/log ufs rw,async 2 2
文档中说,您可以将现有分区转换为使用gjournal,前提是您有一个单独的日志分区,并且现有文件系统的最后一个扇区是空的。在实践中,我发现现有文件系统的最后一个扇区总是满的,但如果你想的话,试着阅读 gjournal(8)
了解详细信息。
您可以使用 tunefs(8)
查看和更改每个UFS文件系统上的设置。这允许您启用和禁用功能;此外,您还可以调整UFS写入文件、管理可用空间和使用文件系统标签的方式。
使用 -p
标志和分区的当前装载点或底层提供程序查看文件系统的当前设置。
xxxxxxxxxx
# tunefs -p /dev/gpt/var
tunefs: POSIX.1e ACLs: (-a) disabled
➊ tunefs: NFSv4 ACLs: (-N) disabled
➋ tunefs: MAC multilabel: (-l) disabled
➌ tunefs: soft updates: (-n) enabled
➍ tunefs: soft update journaling: (-j) enabled
➎ tunefs: gjournal: (-J) disabled
tunefs: trim: (-t) disabled
tunefs: maximum blocks per file in a cylinder group: (-e) 4096
tunefs: average file size: (-f) 16384
tunefs: average number of files in a directory: (-s) 64
➏ tunefs: minimum percentage of free space: (-m) 8%
tunefs: space to hold for metadata blocks: (-k) 6408
tunefs: optimization preference: (-o) time
➐ tunefs: volume label: (-L)
许多可用的设置与我们没有涵盖的特定安全功能有关。像MAC限制➋和所有不同类型的ACL➊这样的主题占据了整本书。但我们可以看到,这个文件系统使用软更新➌和软更新日志➍,尽管它不使用gjournal➎。我们得到了最小的可用空间量➏。最后,我们得到了不存在的UFS标签➐。
使用 tunefs(8)
更改未安装文件系统上的任何这些设置。方便的是,tunefs(8)
显示命令行标志来寻址每个。在更改文件系统的设置之前,我通常会引导到单用户模式。
您可能会注意到,您可以调整各种文件系统内部,如块排列和文件系统几何结构。不要。在FreeBSD使用的二十多年里,我从未见过有人通过摆弄这些旋钮来改善他们的处境。我多次看到人们摆弄这些旋钮,破坏了他们的一天。
但是,让我们看看您可能实际需要启用和禁用的设置。
使用 -j
标志启用或禁用文件系统上的软更新日志记录。这会自动启用软更新。
xxxxxxxxxx
# tunefs -j enable /dev/gpt/var
Using inode 5 in cg 0 for 33554432 byte journal
tunefs: soft updates journaling set
要禁用软更新日志记录,请使用 disable
关键字。
xxxxxxxxxx
# tunefs -j disable /dev/gpt/var
Clearing journal flags from inode 5
tunefs: soft updates journaling cleared but soft updates still set.
tunefs: remove .sujournal to reclaim space
非日志文件系统上的软更新日志只会混淆问题。挂载文件系统,并删除文件系统根目录中的 .sujournal 文件。请注意,关闭日记会使软更新仍然存在。使用 -n enable
和-n disable
打开和关闭软更新(无日志记录)。
UFS保留了每个分区的8%,以便有空间重新排列文件以获得更好的性能。我在第249页的“UFS空间预订”中进一步讨论了这一点。如果要更改此百分比,请使用 -m
标志。在这里,我告诉文件系统只保留5%的磁盘。
xxxxxxxxxx
# tunefs -m 5 /dev/gpt/var
tunefs: minimum percentage of free space changes from 8% to 5%
tunefs: should optimize for space with minfree < 8%
您现在应该有更多可用的磁盘空间。此外,UFS将运行得更慢,因为它总是尽可能紧密地打包文件系统。
固态磁盘使用磨损均衡(wear-leveling)来延长其使用寿命。如果文件系统在每个块不再使用时通知SSD,磨损均衡效果最佳。TRIM协议处理此通知。使用 -t
标志在SSD支持的文件系统上启用TRIM支持。
xxxxxxxxxx
# tunefs -t enable /dev/gpt/var
tunefs: issue TRIM to the disk set
为了获得最佳效果,请为固态驱动器上的每个分区启用TRIM。使用 newfs -E
在文件系统创建时启用TRIM。
您可以使用 -L
标志将UFS标签应用于现有的文件系统。
xxxxxxxxxx
# tunefs -L scratch /dev/ada3s1e
不要混淆UFS和GPT标签——你只会把自己弄糊涂。
您的虚拟机空间不足?将磁盘放大,并扩展最后一个分区以覆盖该空间,如第10章所述。但是那个分区上的文件系统呢?这就是 grows(8)
发挥作用的地方。
growfs(8)
命令扩展现有的UFS文件系统以填充它所在的分区。给growfs一个参数,即文件系统的设备节点。如果你喜欢,可以使用标签。
xxxxxxxxxx
# growfs /dev/gpt/var
It's strongly recommended to make a backup before growing the file system.
OK to grow filesystem on /dev/gpt/var from 50.0GB to 100GB? [Yes/No] ➊yes
super-block backups (for fsck_ffs -b #) at:
19233792, 20516032, 21798272, 23080512, 24362752,
--snip--
当 grows(8)
请求确认时➊,你必须输入完整的单词 yes
。任何其他答案,包括像许多其他程序接受的简单 y
,都会取消操作。确认操作后,growfs(8)
将根据需要添加额外的块、超级块和索引节点来填充分区。
如果你不想让文件系统填满整个分区,你可以用 -s
指定一个大小。在这里,我将这个分区扩展到80GB。
xxxxxxxxxx
# growfs -s 80g /dev/gpt/var
我强烈建议您将文件系统设置为与底层分区相同的大小,除非您想让同事打你耳光。
您可以随时拍摄UFS文件系统的映像;这被称为 snapshot——快照。您可以对文件系统进行快照,擦除和更改一些文件,然后从快照中复制未更改的文件。像 dump(8)
这样的工具使用快照来确保一致的备份。UFS快照不如ZFS快照强大或灵活,但在其限制范围内,它们是一种可靠的工具。
UFS快照需要软更新,但与软更新日志不兼容。每个文件系统最多可以有20个快照。
快照允许您获取已编辑或删除文件的旧版本。通过将文件作为内存设备挂载来访问快照的内容。我将在第13章讨论存储设备。
使用 mksnap_ffs(8)
创建快照。此程序假设您想对当前工作目录所在的文件系统进行快照。请将快照位置作为参数。快照传统上位于文件系统根目录的 .snap 目录中。如果您使用的是自动创建和删除快照的工具,如 dump(8)
,请检查那里的快照文件。不过,如果你不喜欢这个位置,你可以把它们放在你要拍摄快照的文件系统上的任何地方。在这里,我拍摄了 /home 文件系统的快照:
xxxxxxxxxx
# cd /home
# mksnap_ffs .snap/beforeupgrade
快照使用磁盘空间。您无法拍摄完整文件系统的快照。
快照只是一个文件。删除该文件,即可销毁快照。
快照是文件,您可以将它们放在文件系统上的任何位置。这意味着很容易失去它们。使用 find(1)
和 -flags snapshot
选项查找文件系统上的所有快照。
xxxxxxxxxx
# find /usr -flags snapshot
/usr/.snap/beforeupgrade
/usr/.snap/afterupgrade
/usr/local/testsnap
这是我的杂散快照!
快照记录当前文件系统和拍摄快照时存在的文件系统之间的差异。拍摄快照后,每次文件系统更改都会增加快照的大小。如果删除文件,快照将保留该文件的副本,以便以后恢复。
这意味着从带有快照的文件系统中删除数据实际上并没有释放空间。如果您有 /home 分区的快照,并且删除了一个文件,则删除的文件将添加到快照中。
确保带有快照的文件系统始终有足够的可用空间。如果您尝试拍摄快照,但 mksnap_ffs(8)
抱怨由于没有空间而无法拍摄,则您可能已经拥有该文件系统的20个快照。
从硬件故障到系统管理不当,一切都可能损坏您的文件系统。UFS的所有弹性技术都旨在快速恢复数据完整性,但没有什么能完全保证完整性。
让我们讨论一下FreeBSD如何保持每个UFS文件系统的整洁。
当你关闭FreeBSD系统时,内核会将其所有数据同步到硬盘驱动器,标记磁盘干净,然后关闭。这是由一个名为 syncer 的内核进程完成的。在系统关闭期间,同步器会报告其同步硬盘的进度。
在关机期间,您会从同步器中看到奇怪的东西。syncer遍历需要同步到磁盘的vnode列表,使其能够支持所有文件系统,而不仅仅是UFS。由于软更新,将一个vnode写入磁盘可能会生成另一个需要更新的脏vnode。您可以看到写入磁盘的缓冲区数量从高值迅速下降到低值,并可能在零和低值之间反弹一到两次,因为系统真正地同步了硬盘驱动器。
如果同步器没有机会完成,或者由于你笨拙的摸索,同步器根本无法运行,那么你会得到一个脏文件系统。
不,磁盘在使用过程中不会变脏(尽管磁盘上的灰尘会很快损坏它,加水也无济于事)。一个肮脏的UFS分区处于一种不确定状态;操作系统已要求将信息写入磁盘,但数据尚未完全存储在物理介质上。部分数据块可能已被写入,索引节点可能已被编辑,但数据未被写出,或两者的任何组合。实时文件系统几乎总是脏的。
如果具有脏文件系统的主机发生故障,例如,由于死机或Bert绊倒电源线,当系统再次启动时,文件系统仍然是脏的。内核拒绝挂载脏文件系统。
清理文件系统可以恢复数据完整性,但并不一定意味着所有数据都在磁盘上。如果系统死机时文件被半写到磁盘,则文件将丢失。没有什么可以恢复丢失的文件的一半,而磁盘上的那一半基本上是无用的。
当FreeBSD尝试挂载日志文件系统时,日志文件系统应该会自动恢复。如果文件系统无法恢复,或者您没有日志,则需要使用传奇的 fsck(8)
。
fsck(8)
程序检查UFS文件系统,并尝试验证每个文件是否都附加到正确的索引节点和正确的目录中。这就像验证数据库的引用完整性。如果文件系统仅受到轻微损坏,fsck(8)
可以自动恢复完整性并使文件系统重新投入使用。
修复损坏的文件系统需要时间和内存。运行 fsck(8)
需要大约700MB的RAM来分析1TB的文件系统。大多数计算机系统都有相当比例的内存和存储系统:很少有主机有512MB的RAM和PB的磁盘。但是你应该知道,有可能创建一个如此大的UFS文件系统,以至于系统没有足够的内存来修复它。
重启时这种自动 fsck
偶尔会失败。当您检查控制台时,您将看到一个单用户模式提示和一个手动运行 fsck(8)
的请求。
首先用 fsck -p
整理(preening)文件系统。这会自动纠正一堆不太严重的错误,而无需征得您的批准。预处理很少会导致数据丢失。这通常是成功的,但如果它不起作用,它会要求你运行一个“完整的fsck”
如果在命令提示符下输入 fsck
,fsck(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和超级块,并识别每个不一致。它要求您键入 y
或n
来批准或拒绝每一项更正。你拒绝的任何改变都必须通过其他方式来修复自己。你可能会在控制台上花几个小时键入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
,你有什么选择?好吧,fsdb(8)
和 clri(8)
允许您调试文件系统并将文件重定向到正确的位置。您可以将文件还原到其正确的目录和名称。然而,这很难做到,而且只建议用于Secret Ninja Filesystem Masters。
后台(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)
。
以读写方式挂载脏分区会损坏数据。请注意,这个句子中没有像 may 和 could 这样的词。还要注意,我没有使用 recoverable。安装脏文件系统可能会使您的计算机死机。它可能会破坏分区上所有剩余的数据,甚至破坏底层文件系统。强制对脏文件系统进行读写挂载是非常糟糕的juju。不要这样做。
所有这些不同的 fsck(8)
问题和情况都可能发生,但FreeBSD什么时候使用每个命令?FreeBSD使用以下条件来决定何时以及如何在文件系统上执行 fsck(8)
:
fsck(8)
的情况下挂载它。fsck(8)
。fsck(8)
。如果文件系统损坏严重,FreeBSD会停止检查并请求您的干预。您可以运行 fsck -y
或手动批准每次更正。fsck(8)
检查。如果损坏轻微,FreeBSD可以在多用户模式下使用后台 fsck(8)
。fsck(8)
,FreeBSD会中断引导并请求手动fsck(8)
。配置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。只要你知道你选择了哪一个,这两种方法都有效。我是一名网络管理员,所以在这些例子中,你会因为我的偏见而受苦,不管我的技术编辑怎么想。
xxxxxxxxxx
# df -H
➊ Filesystem Size Used Avail Capacity Mounted on
➋ /dev/gpt/root 1.0G 171M 785M 18% /
devfs 1.0k 1.0k 0B 100% /dev
/dev/gpt/var 1.0G 64M 892M 7% /var
/dev/gpt/tmp 1.0G 8.5M 948M 1% /tmp
➌ /dev/gpt/usr 14G 13.8G 203M 98% /usr
第一行显示了提供程序名称的列标题➊、分区大小、已使用的空间量、可用空间量、已使用空间百分比和装载点。我们可以看到,标记为 /dev/gpt/root ➋的分区大小只有1GB,但只有171MB,剩下785MB可用。它已满18%,安装在/上。
如果你的系统和我的一样,磁盘使用率就会无缘无故地持续增长。请查看此处的 /usr 分区➌。已满98%。您可以使用ls-l标识单个大文件,但在系统中的每个目录上递归执行此操作是不切实际的。
du(1)
程序显示单个目录中的磁盘使用情况。它的初始输出令人望而生畏,可能会吓跑没有经验的用户。在这里,我们使用 du(1)
来找出是什么占用了我主目录中的所有空间:
xxxxxxxxxx
# cd $HOME
# du
1 ./bin/RCS
21459 ./bin/wp/shbin10
53202 ./bin/wp
53336 ./bin
5 ./.kde/share/applnk/staroffice_52
6 ./.kde/share/applnk
--snip--
这会一直持续下去,显示每个子目录并以块为单位给出其大小。给出了每个子目录的总数——例如,$HOME/bin 的内容总计53336个块,约53MB。我可以坐下来让 du(1)
列出每个目录和子目录,但这样我就必须挖掘出比我真正想挖掘的更多的信息。而且块并不是一种方便的测量方法,尤其是当它们被左对齐打印时。
让我们清理一下。首先,du(1)
支持一个 -h
标志,就像 df
一样。此外,我不需要看到每个子目录的递归内容。我们可以使用du的 -d
标志控制显示的目录数量。此标志接受一个参数,即要显式列出的目录数量。例如,-d0
深入一个目录,并给出一个目录中文件的简单小计。
xxxxxxxxxx
# du -h -d0 $HOME
14G /home/mwlucas
我的主目录中有14 GB的数据?让我们深入一层,找出最大的子目录。
xxxxxxxxxx
# du -h -d1
38K ./bin
56M ./mibs
--snip--
13G ./startrekgifs
--snip--
显然,我必须在其他地方寻找存储空间,因为我的主目录中的数据太重要了,无法删除。也许我应该在这个主机下扩展虚拟磁盘。
如果您不太依赖 -h
标志,则可以使用sort(1)通过类似 du -kxd 1 | sort -n
的命令查找最大的目录。
无论你做了多少计划,最终你的硬盘都会被填满。您需要添加磁盘。在使用新硬盘之前,您必须对驱动器进行分区,创建文件系统,挂载这些文件系统,并将数据移动到其中。
像最初安装时一样,仔细考虑新磁盘分区和文件系统的设计。在安装时正确分区磁盘比返回并重新分区磁盘上的数据要容易得多。
备份,备份,备份! 在使用磁盘之前,请确保您有完整的备份。一个愚蠢的胖手指错误可以摧毁你的系统!虽然您很少计划重新格式化根文件系统,但如果发生这种情况,您希望非常非常快速地恢复。
虽然您可以以任何方式对磁盘进行分区,但我建议新磁盘使用与主机其余部分相同的分区方案。一个磁盘用MBR分区,另一个用GPT分区,这很烦人。在这个例子中,我将使用GPT。
决定如何分割磁盘。这是一个1TB的磁盘。100GB将用于扩展的 /tmp 。我将为我的新数据库分区分配500GB。剩余的空间被分隔开,但被标记为 emergency 。我不会在那个空间中放置文件系统;它就在那里,以防我需要进行完整的内存转储或必须将一些文件放在某个地方。我把它放在数据库分区旁边,这样我就可以在需要时扩展数据库分区。我可以不划分紧急空间,但我希望它有一个GPT标签,这样我的其他系统管理员就会意识到这个空闲空间不是偶然的。
首先销毁磁盘上的任何分区方案并创建GPT方案。
xxxxxxxxxx
# gpart destroy -F da3
da3 destroyed
# gpart create -s gpt da3
da3 created
现在创建100GB /tmp 和500GB数据分区,并将其余部分转储到紧急分区中。
xxxxxxxxxx
# gpart add -t freebsd-ufs -l tmp -s 100g da3
da3p1 added
# gpart add -t freebsd-ufs -l postgres -s 500g da3
da3p2 added
# gpart add -t freebsd-ufs -l emergency da3
da3p3 added
通过 gpart show
检查你的作品。
xxxxxxxxxx
# gpart show -lp da3
=> 40 1953525088 da3 GPT (932G)
40 209715200 da3p1 tmp (100G)
209715240 1048576000 da3p2 postgres (500G)
1258291240 695233888 da3p3 emergency (332G)
在每个分区上创建文件系统。
xxxxxxxxxx
# newfs -j /dev/gpt/tmp
# newfs -j /dev/gpt/postgres
由于 /tmp 在每次启动时都会被清空,我不希望在 /tmp 上使用软更新日志记录。相反,我会挂载 /tmp async并在启动时运行 newfs /dev/gpt/tmp
。很多时候,newfs(8)
比 rm(1)
快。
现在告诉 /etc/fstab 您的文件系统。我们在第10章讨论 /etc/fstab 的格式。
xxxxxxxxxx
/dev/gpt/postgres /usr/local/etc/postgres ufs rw 0 2
/dev/gpt/tmp /tmp ufs rw 0 2
FreeBSD将在启动时识别文件系统,或者您可以在命令行挂载这些新分区。不过,暂时不要重新启动或挂载分区。首先,您需要将文件移动到这些文件系统。
您可能打算用新磁盘替换或细分现有分区。您需要将新分区挂载到临时挂载点,将文件移动到新磁盘,然后在所需位置重新挂载分区。虽然 /tmp 没有任何文件,但如果我们要安装一个新的数据库文件系统,我们可能有数据库文件要放在那里。
在移动文件之前,请关闭使用它们的任何进程。在复制文件时,您无法成功复制正在更改的文件。如果要移动数据库文件,请关闭数据库。如果你要移动邮件卷轴,请关闭所有邮件程序。这是我建议在单用户模式下进行所有新磁盘安装的重要原因。
现在将新分区装载到临时装载点上。这正是 /mnt 的用途。
xxxxxxxxxx
# mount /dev/gpt/postgres /mnt
现在,您必须将文件从其当前位置移动到新磁盘,而不更改其权限。这对于 tar(1)
来说相当简单。您可以简单地将现有数据映射到磁带或文件,并在新位置解压缩,但这有点笨拙。将一个 tar
倒入另一个tar
中,以避免中间步骤。
xxxxxxxxxx
# tar cfC - /old/directory . | tar xpfC - /tempmount
如果你在聚会上不说Unix,这看起来相当令人震惊。让我们拆除它。首先,我们去旧目录,把所有东西打包。然后,将输出传输到第二个命令,该命令提取新目录中的备份。此命令完成后,您的文件将安装在新磁盘上。例如,要将 /usr/local/etc/postgres 移动到临时挂载在 /mnt 的新分区上,您需要执行以下操作:
xxxxxxxxxx
# tar cfC - /usr/local/etc/postgres . | tar xpfC - /mnt
检查临时装载点,以确保您的文件确实在那里。一旦您确信文件已正确移动,请从旧目录中删除文件,并将磁盘装载到新位置。例如,从 /usr/local/etc/postgres 复制文件后,您可以运行:
xxxxxxxxxx
# rm -rf /usr/local/etc/postgres
# umount /mnt
# mount /usr/local/etc/postgres
您现在可以恢复正常操作。我建议重新启动以验证一切是否完全按照您的预期恢复。
也许你不在乎你的旧数据;您希望拆分现有的文件系统只是为了获得更多空间,并且您打算从备份中恢复数据。那很好。所有FreeBSD文件系统都是可堆叠的。这是一个先进的想法,在日常系统管理中并不是非常有用,但当你试图将一个分区一分为二时,它可能会让你感到痛苦。
例如,假设您在 /usr/src 中有数据。查看磁盘上使用了多少空间,然后在 /usr/src 上挂载一个新的空分区。如果你稍后查看目录,你会看到它是空的。
问题是:旧文件系统上仍然有它所有的原始数据。新文件系统被挂载在旧文件系统的“上方”,所以你只能看到新文件系统。旧文件系统没有比移动数据之前更多的可用空间。如果你卸载新的文件系统并再次检查目录,你会看到数据奇迹般地恢复了!新的文件系统掩盖了较低的文件系统。
虽然您看不到数据,但旧文件系统上的数据仍然会占用空间。如果您添加文件系统以获得空间,并在旧文件系统的一部分上挂载新文件系统,则不会释放原始文件系统上的任何空间。这个故事的寓意是:即使你正在从备份中恢复数据,也要确保从原始磁盘中删除这些数据以恢复磁盘空间。
现在你可以谈论UFS了,让我们来探索ZFS。