14.8.磁盘优化¶
以下章节将讨论可用于磁盘设备的各种优化机制和选项。在许多情况下,带有机械部件的磁盘,如 SCSI 硬盘,将成为降低整个系统性能的瓶颈。虽然可以通过安装一个没有机械部件的硬盘来解决该问题,如固态硬盘,但机械硬盘在短期内不会消失。在磁盘优化时,建议利用 iostat(8)
命令的功能来测试系统的各种变化。这个命令可使用户获得关于系统 IO 的宝贵信息。
14.8.1. sysctl 变量¶
14.8.1.1. vfs.vmiodirenable
¶
sysctl(8) 变量 vfs.vmiodirenable
可以被设置为 0
(关闭)或 1
(开启)。默认情况下,它被设置为 1
。该变量控制系统对目录的缓存方式。大多数目录都很小,在文件系统中只使用一个片段(通常为 1K),在缓冲区缓存中通常为 512 字节。如果关闭这个变量,缓冲区缓存将只缓存固定数量的目录,即使系统有大量的内存。当打开时,这个 sysctl(8) 允许缓冲区缓存使用虚拟内存页面缓存来缓存目录,使所有的内存都可以用于缓存目录。然而,用于缓存目录的最小核心内存是物理页大小(通常是 4K)而非 512 字节。如果系统正在运行某些操作大量文件的服务,建议保持该选项的启用。这些服务可能包括网络缓存、大型邮件系统和新闻系统。开启这个选项通常不会降低性能,即使会浪费内存,但人们应该通过实验来发现问题。
14.8.1.2. vfs.write_behind
¶
sysctl(8) 变量 vfs.write_behind
的默认值是 1
(开启)。这告诉文件系统在收集完整的族时进行媒体写入,这通常发生在写入大的连续文件时。这可以避免在 I/O 性能无益的情况下用脏缓冲区使缓冲区缓存饱和。然而,这可能会拖延进程,在某些情况下应该关闭。
14.8.1.3. vfs.hirunningspace
¶
sysctl(8) 变量 vfs.hirunningspace
决定了在任何给定的实例中,有多少未完成的写 I/O 可以被排到磁盘控制器的系统中。默认值通常是足够的,但在有许多磁盘的机器上,可以尝试把它提高到 4 或 5 兆字节。设置太高的值,超过缓冲区缓存的写入阈值,会导致糟糕的族性能。不要随便设置这个值,因为更高的写入值可能会增加同时进行的读取的延迟。
还有其他各种与缓冲区高速缓存和虚拟页面高速缓存相关的 sysctl(8) 值。不建议修改这些值,因为虚拟系统在自动调整自己方面做得很好。
14.8.1.4. vm.swap_idle_enabled
¶
sysctl(8) 变量 vm.swap_idle_enabled
在有许多活跃的登录用户和大量空闲进程的大型多用户系统中非常有用。这样的系统往往会对空闲的保留内存持续产生压力。通过 vm.swap_idle_threshold1
和 vm.swap_idle_threshold2
打开这个功能并调整交换滞后(以空闲秒为单位),比正常的分页算法更快地降低与空闲进程相关的内存页的优先级。这将给分页守护程序带来帮助。只有在需要时才打开这个选项,因为这样做的代价是尽早对内存进行预分页,而不是晚分页,这会消耗更多的交换空间和磁盘带宽。在小系统中,这个选项的影响是可确定的,但是在一个已经在进行适度分页的大系统中,这个选项允许虚拟系统轻松地将整个进程放入和取出内存。
14.8.1.5. hw.ata.wc
¶
关闭 IDE 写缓存可以减少对 IDE 磁盘的写入带宽,但由于硬盘供应商引入的数据一致性问题,有时可能是必要的。问题是,有些 IDE 硬盘会在写入完成时撒谎:在打开 IDE 写缓存的情况下,IDE 硬盘会不按顺序地向磁盘写入数据,在磁盘负载较重的情况下,有时会无限期地延迟写入一些块。崩溃或断电可能导致严重的文件系统损坏。可通过观察 sysctl(8) 变量 hw.ata.wc
来检查系统上的默认情况。如果 IDE 写缓存被关闭,可以在 /boot/loader.conf
中把这个只读变量设置为 1
,以便在启动时启用它。
更多信息,请参考 ata(4) 的手册。
14.8.1.6. SCSI_DELAY
(kern.cam.scsi_delay
)¶
内核配置选项 SCSI_DELAY
可以用来减少系统启动时间。默认值是相当高的,可能会在启动过程中造成 15 秒的延迟。把它减少到 5 秒通常对现代硬盘有效。应该使用 kern.cam.scsi_delay
调整启动时间。可优化的内核配置选项接受值的单位是毫秒而非秒。
14.8.2.软更新¶
要对文件系统进行微调,可以使用 tunefs(8)。这个程序有许多不同的选项。要打开或关闭软更新,请使用:
# tunefs -n enable /filesystem
# tunefs -n disable /filesystem
文件系统在挂载时不能用 tunefs(8) 来修改。在单用户模式下,启用软更新的好时机是在任何分区被挂载之前。
软更新被推荐用于 UFS 文件系统,因为它通过使用内存缓存极大地提高了元数据的性能,主要是文件的创建和删除。软更新有两个需要注意的缺点:首先,软更新保证了文件系统在崩溃情况下的一致性,但可能很容易比物理磁盘的更新晚几秒甚至一分钟。如果系统崩溃了,尚未写入的数据可能会丢失。其次,软更新延迟了文件系统块的释放。如果根文件系统几乎满了,执行一个大的更新,如 make installworld
,会导致文件系统的空间耗尽,更新失败。
14.8.2.1.关于软更新的更多细节¶
元数据更新是对非内容数据的更新,如节点或目录。有两种传统的方法来将文件系统的元数据写回磁盘。
过去,默认行为是同步写出元数据更新。如果一个目录发生了变化,系统会等待,直到该变化被实际写入磁盘。文件数据缓冲区(文件内容)通过缓冲区缓存,随后异步备份到磁盘。这种实现方式的优点是操作安全。如果在更新过程中出现了故障,元数据总是处于一致的状态。一个文件要么被完全创建,要么根本就没有。如果一个文件的数据块在崩溃时还没有从缓冲区缓存中找到它们的位置,fsck(8) 会识别出这一点,并通过将文件长度设置为 0
来修复文件系统。此外,这个实现是清晰和简单的。缺点是,元数据的改变很慢。例如,rm -r
按顺序触及一个目录中的所有文件,但每个目录的变化都会同步写入磁盘。这包括对目录本身的更新,对节点表的更新,以及可能对文件分配的间接块的更新。类似的考虑也适用于使用 tar -x
解开大型层次结构。
第二种方法是使用异步的元数据更新。这是用 mount -o async
挂载的 UFS 文件系统时的默认做法。由于所有的元数据更新也要通过缓冲区缓存,它们将与文件内容数据的更新混在一起。这个实现的好处是不需要等待每个元数据的更新被写入磁盘,所以所有引起大量元数据更新的操作都比同步情况下快得多。这个实现仍然是清晰和简单的,所以代码中出现错误的风险很低。缺点是不能保证文件系统的一致状态 如果在更新大量元数据的操作中出现故障,比如断电或有人按下复位按钮,文件系统将被留在不可预测的状态中。当系统再次启动时,没有机会检查文件系统的状态,因为文件的数据块可能已经被写入磁盘,而节点表或相关目录的更新却没有。因为磁盘上没有必要的信息,所以不可能使用 fsck(8) 来清理由此产生的混乱。如果文件系统已经被破坏得无法修复,唯一的选择就是重新格式化并从备份中恢复。
这个问题的通常解决方案是实现 脏区记录,这也被称为 日志。元数据的更新仍然是同步写入的,但只写入磁盘的一个小区域。后来,它们被移到适当的位置。由于日志区是磁盘上的一个小的、连续的区域,即使在繁重的操作中,磁盘磁头也不会有长距离的移动,所以这些操作比同步更新要快。此外,实现的复杂性是有限的,所以出现错误的风险很低。一个缺点是,所有的元数据都要写两次,一次写到日志区域,一次写到适当的位置,所以可能导致性能“下降”。另一方面,在崩溃的情况下,所有悬而未决的元数据操作可以迅速回滚,或者在系统再次启动后从日志区域完成,从而导致文件系统的快速启动。
Berkeley FFS 的开发者 Kirk McKusick 用软更新解决了这个问题。所有悬而未决的元数据更新被保存在内存中,并以排序的顺序写入磁盘(“有序元数据更新”)。这样做的效果是,在大量元数据操作的情况下,一个项目的后期更新会“抓住”仍在内存中且尚未被写入磁盘的早期更新。在更新被写入磁盘之前,所有的操作一般都是在内存中进行的,而且数据块会根据它们的位置进行排序,这样它们就不会在磁盘上领先于它们的元数据。如果系统崩溃,一个隐含的“日志回退”会使所有没有被写入磁盘的操作看起来就像它们从未发生一样。一个一致的文件系统状态被维持,看起来是 30 到 60 秒之前的状态。所用的算法保证了所有正在使用的资源在它们的块和节点中都被标记成这样。在崩溃之后,唯一发生的资源分配错误是资源被标记为“已使用”(used),而实际上是“已释放”(free)。fsck(8) 识别这种情况,并释放不再使用的资源。在文件系统崩溃后,用 mount -f
强行挂载文件系统的脏状态是安全的。为了释放可能未使用的资源,fsck(8) 需要在稍晚的时间运行。这就是 后台 fsck(8) 的想法:在系统启动时,只记录文件系统的 快照,之后再运行 fsck(8)。然后,所有的文件系统都可以“dirty”挂载,所以系统的启动在多用户模式下进行。然后,后台 fsck(8) 被安排在所有需要这样做的文件系统上,以释放可能未使用的资源。不使用软更新的文件系统仍然需要常规的前台 fsck(8)。
其优点是元数据操作的速度几乎与异步更新一样快,而且比 记录 要快,因为记录要写两次元数据。缺点是代码的复杂性,较高的内存消耗,以及一些特异性的问题。在崩溃之后,文件系统的状态显得有些“旧”。在标准的同步方法会导致一些零长度的文件在 fsck(8) 之后仍然存在的情况下,这些文件在软更新中根本就不存在,因为元数据和文件内容都没有被写入磁盘。在更新被写入磁盘之前,磁盘空间不会被释放,这可能发生在运行 rm(1) 之后的一段时间。当在一个没有足够空闲空间容纳所有文件的文件系统上安放大量数据时,这可能会导致问题。