是否有必要显式刷新 HDD 磁盘写入缓存?

Jür*_*gen 12 linux command-line cache usb unmounting

抽象的

有时,Linux 内核不知道外部 USB 存储设备的驱动器写入缓存。在这种情况下是否有必要在分离这些设备之前显式刷新这些缓存?

例子

我使用 WD Elements 外置 USB 硬盘,上面hdparm -I写着

...
Commands/features:
    Enabled Supported:
       ...
       *    Write cache
...
Run Code Online (Sandbox Code Playgroud)

hdparm -W

...
 write-caching =  1 (on)
Run Code Online (Sandbox Code Playgroud)

另一方面,当我插入驱动器时,我收到以下内核消息:

... No Caching mode page found
... Assuming drive cache: write through
Run Code Online (Sandbox Code Playgroud)

根据Kyle Jones 的这个回答,这些内核消息表明内核假设它的写操作将直接进入盘片。

Linux 内核文档中的文件Documentation/block/queue-sysfs.txt 的“write_cache (RW)”部分描述了内核假设通过缓存写入模式的含义(感谢Wayne Conrad):

...“直写”,...还将消除内核发出的缓存刷新。

问题

到目前为止,我从 Linux 系统分离外部 USB 存储设备的标准方法是卸载它上所有已安装的分区,等到驱动器的 LED 停止闪烁,物理拔下 USB 连接器,如果这不会关闭电源设备(有些具有单独的电源),以明确关闭电源。

这种方法是否安全,或者它是否意味着在驱动器写入缓存中丢失未刷新数据的风险,特别是如果内核不知道该缓存?

在后一种情况下,似乎建议在卸载后通过发送 SCSI 同步命令显式刷新驱动器上的写入缓存。例如sg_sync,可以使用 sg3-utils 附带的 which来完成:

sg_sync <device>
Run Code Online (Sandbox Code Playgroud)

这能解决问题吗?或者是否应该通过对设备的 SCSI 停止命令来补充?

后者可以与(再次 sg3-utils)一起发布:

sg_start 0 -r <device>
Run Code Online (Sandbox Code Playgroud)

sg_syncsg_start合适的工具用于此目的或者是它更好地使用的,我提以下部分“相关法”或做别的事情来处理这个问题的工具和方法呢?

要指出此问题的相关性,请参阅ack 的此评论

请注意:我不是在寻找一种主要通过软件来降低硬盘转速或关闭硬盘的方法——拔掉驱动器并使用电源开关在这方面被证明是相当可靠的。相反,我正在寻找一种方法来保证所有写入的数据在驱动器关闭之前都已进入非易失性存储,包括缓存在驱动器上的数据。

相关方法

在下文中,我对我发现与刷新驱动器缓存,或更一般地说,与“安全删除”可移动存储设备相关的方法进行了一些评论。在那里,一方面是现有 Linux 系统上相关工具的可用性。这很重要,因为并不总是可以简单地安装丢失的软件。

桌面环境:一些桌面环境提供小部件来“安全地移除”外部 USB 存储设备。例如,请参阅以下问题:

和相关帖子。

根据实现,这些小部件似乎会关闭设备电源,但我没有找到任何参考资料来说明它们是否会导致设备预先刷新其缓存,尤其是在内核不知道外部驱动器缓存的情况下.

另外,很多Linux系统(比如纯服务器)都没有安装任何桌面环境。所以这种方法并不总是可用的。

udisks:根据jimmij 的这个回答,可以从命令行使用udisksFreedesktop.org来“安全删除”外部 USB 存储设备:

udisks --unmount /dev/sda1
udisks --detach /dev/sda
Run Code Online (Sandbox Code Playgroud)

Totor 的这个出色回答描述了该udisks --detach命令的作用:

  • 发送 SCSI 同步缓存命令,
  • 发送 SCSI 停止命令,
  • 解绑 USB 存储内核驱动程序,
  • 暂停 USB 设备(电源),
  • 从其 USB 端口逻辑禁用/删除它。

所以它明确地处理驱动器上的缓存。但是,udisks仅在我必须处理的大约一半 Linux 系统上可用。

udisksctl: udisks 的最新版本提供程序udisksctl,可用于替代udisks上面显示的命令。这又是根据jimmij 的回答

udisksctl unmount -b <partition>
udisksctl power-off -b <device>
Run Code Online (Sandbox Code Playgroud)

相同的答案还引用了udisksctl(1) 手册页中对power-off命令的描述:

关机

安排驱动器安全移除和断电。在操作系统方面,这包括确保没有进程正在使用驱动器,然后请求将动态缓冲区和缓存提交到稳定存储。

不幸的是,这并没有指定“运行中的缓冲区和缓存”是否包括内核不知道的外部驱动器缓存。但它很可能在这方面udisksctl遵循其前身udisks

不幸的是,udisksctl它在现有系统上的可用性相当低(umount例如,与 的可用性相比)。

弹出:根据其手册页,命令行工具eject可用于在软件控制下弹出可移动媒体。受影响的分区将根据需要预先卸载。

虽然这个工具显示了在可移动介质上的“安全删除”多次讨论,例如见这个问题通过LGenzelis和这个问题由k.Cyborg,没有证据表明它确实不止在这方面的卸载。

此外,我怀疑这个工具更多地关注媒体和媒体托盘,而不是设备。这可能是它简单地因错误消息而死亡的原因

eject: unable to eject
Run Code Online (Sandbox Code Playgroud)

当它应用于我在上面的介绍性示例中提到的 WD Elements USB 驱动器时。但是,它在某些 USB 记忆棒上成功。

尽管如此,作为 linux-utils 的一部分,这个工具是高度可用的。

SG3-utils的:这些方案sg_syncsg_start已经在上面讨论。

这个quirks 对 Ubuntu 错误报告的评论表明,在udisks内部使用 sg3-utils 将其 SCSI 同步和停止命令发送到设备。

在我看来,sg3-utils 比 udisks 具有更广泛的可用性。但这只是一个模糊的个人印象。

sdparm: Yan Li 的这个网页讨论了“在 Linux 中安全删除 USB 硬盘驱动器”的过程。为此,它推荐了一个脚本该脚本原则上使用以下sdparm命令来刷新驱动器缓存并停止(降速?)USB HDD:

sdparm --command=sync <device>
sdparm --command=stop <device>
Run Code Online (Sandbox Code Playgroud)

这些似乎与上面讨论的sg_syncsg_start命令相当,并且可以用作后者的替代品。

hdparm:基本上是一种管理 ATA 驱动器的工具,hdparm从某种意义上说,对于 USB 驱动器来说是一个外来对象,因为后者在 Linux 中主要被称为 SCSI 设备。在我们的 WD Elements 示例中,有一个 ATA HDD 位于 SCSI 到 ATA 转换层(SAT 层)的后面。有关更多详细信息,请参阅Mikko Rantalainen 的这个答案。根据 SAT 的实施,hdparm可以使用有限的功能来操纵这些驱动器。

如果支持,可以使用命令

hdparm -F <device>
hdparm -Y <device>
Run Code Online (Sandbox Code Playgroud)

刷新驱动器上的缓存并停止驱动器。如果这是不可能的,人们可能会想到使用

hdparm -W0 <device>
Run Code Online (Sandbox Code Playgroud)

作为刷新缓存的解决方法。但请注意:此命令实际上旨在关闭驱动器上的写入缓存。因此,应该确保它实际刷新而不是简单地删除迄今为止累积的缓存内容。

对于 WD Elements 驱动器,这些命令都不起作用:相反,它们报告bad/missing sense data.

sysfs:散布在网络上,有一些方法可以解除外部 USB 驱动器与其驱动程序的绑定,从系统中注销驱动器,或者通过操纵 Linux 内核在其 sysfs 中公开的设备属性来关闭它。

这是Debian 论坛中 bash这篇文章中的一个示例:

echo "auto" > "/sys/bus/usb/devices/usb1/1-5/power/level"
echo "1-5:1.0" > /sys/bus/usb/devices/1-5\:1.0/driver/unbind
Run Code Online (Sandbox Code Playgroud)

echo "1" > "/sys/bus/usb/devices/usb1/1-5/remove"
Run Code Online (Sandbox Code Playgroud)

或者从托尼乔治的这个答案

echo 'offline' > /sys/block/sdb/device/state
echo '1' > /sys/block/sdb/device/delete
Run Code Online (Sandbox Code Playgroud)

我对内核开发人员在使用这些属性时所想到的概念知之甚少,无法判断这些代码片段。但是我怀疑它们是否有助于刷新内核不知道的驱动器写入缓存。

此外,似乎这些食谱中至少有一些已经过时了。在这方面,参见2019-01-25的内核文档中“ Sysfs规则”的第一段:

内核导出的 sysfs 导出内部内核实现细节并依赖于内部内核结构和布局。内核开发人员一致认为,Linux 内核不提供稳定的内部 API。因此,sysfs 接口的某些方面在内核版本中可能不稳定。

卸载并等待:这是我在上面的问题中提到的方法:它包括卸载和随后的等待,直到驱动器上的(大概)写入活动最终停止。下一步也是最后一步是希望在此过程中刷新驱动器上的写入缓存。

这个问题的重点是澄清这是否是一种安全的方法(对于数据)。

无论如何,这种方法有一个很大的优势,它很容易,在我知道的任何 Linux 系统上都可以使用,而且umount命令的语法几十年来没有改变。

进一步阅读

在这些问题的上下文中可以找到一些关于“弹出”和“安全移除”USB 或其他外部存储设备的一般性讨论:

此外,Luis Alvarado 在此回答中描述了 Ubuntu 桌面环境提供的“卸载”、“弹出”和“安全删除驱动器”选项之间的区别,特别是“安全删除驱动器”不仅仅是一个卸载。关于后者,另见

许多贡献者认为,一旦卸载外部存储设备,就可以“安全地移除”外部存储设备。这里有一些例子:

除了卸载之外,一些贡献还侧重于降低外部 HDD 的转速。例如,参见winchendonsprings 的这个问题。在此评论中,sourcejedi 指出,降低 USB 驱动器的转速会降低其对机械干扰的脆弱性。

其他人旨在关闭 USB 驱动器:

在以下贡献中,关于存储设备的“安全删除”明确提到了驱动器缓存:

后者是我发现的唯一一个贡献,它指出了由于内核对外部驱动器写入缓存的错误假设而可能引起的损害。这正是这个问题的重点。根据该评论,仅卸载驱动器不足以防止这种损坏。进一步的细节和可能的正确方法将不胜感激。

小智 -1

我一直在互联网上寻找有关通过 USB 连接的外部驱动器上写入缓存的启动错误的答案。要回答第一个问题,a)是的,在拔出 USB 之前刷新缓冲区/缓存非常重要。我建议,无论您喜欢与否,您都需要卸载外部 USB 驱动器,以便操作系统可以确保所有内容都写入硬盘驱动器。b)阅读我读过的所有内容,并发现答案就在我面前。“我相信关于未找到缓存页面代码的错误,假设是直写”(在使用journalctl -b命令时发现)是通过USB连接外部硬盘驱动器启动系统所固有的。这是一个好的设计问题!USB设备可以随时拔出。如果他们没有操作系统的意识或没有足够的时间准备,数据就会损坏!因此,journalctl 记录的错误将被我忽略,因为它是设计的一部分。对我来说,避免这些错误的唯一方法是不要通过 /etc/fstab 挂载它们并让它在插入时自动挂载。