Jua*_*nde 61 linux filesystems disk
我以前认为文件更改直接保存到磁盘中,即我关闭文件并决定单击/选择保存。然而,在最近的一次谈话中,我的一个朋友告诉我这通常不是真的。操作系统(特别是我们谈论的 Linux 系统)将更改保留在内存中,并且它有一个守护进程,实际上将内容从内存写入磁盘。
他甚至举了外部闪存驱动器的例子:它们被安装到系统中(复制到内存中),有时会发生数据丢失,因为守护进程尚未将内容保存到闪存中;这就是我们卸载闪存驱动器的原因。
我对操作系统的功能一无所知,因此我完全不知道这是否属实以及在何种情况下。我的主要问题是:这是否像在 Linux/Unix 系统(以及其他操作系统)中描述的那样发生?例如,这是否意味着如果我在编辑和保存文件后立即关闭计算机,我所做的更改很可能会丢失?也许这取决于磁盘类型——传统硬盘与固态磁盘?
该问题专门针对具有磁盘来存储信息的文件系统,即使任何澄清或比较都受到好评。
ilk*_*chu 71
如果我在编辑和保存文件后立即关闭计算机,我的更改很可能会丢失吗?
他们可能是。我不会说“最有可能”,但可能性取决于很多事情。
提高文件写入性能的一种简单方法是让操作系统只缓存数据,告诉(向)应用程序进行写入,然后实际进行写入。如果同时进行其他磁盘活动,这将特别有用:操作系统可以优先读取并稍后进行写入。它还可以完全消除实际写入的需要,例如,在此后快速删除临时文件的情况下。
如果存储速度很慢,缓存问题会更加明显。将文件从快速 SSD 复制到慢速 U 盘可能会涉及大量写入缓存,因为 U 盘无法跟上。但是您的cp
命令返回速度更快,因此您可以继续工作,甚至可以编辑刚刚复制的文件。
当然,像这样的缓存有你注意到的缺点,一些数据可能会在实际保存之前丢失。如果他们的编辑器告诉他们写入成功,但该文件实际上不在磁盘上,用户会感到恼火。这就是为什么有fsync()
系统调用,它应该只在文件实际命中磁盘后返回。在向用户报告写入成功之前,您的编辑器可以使用它来确保数据正常。
我说,“应该”,因为驱动器本身可能会告诉操作系统同样的谎言并说写入已完成,而该文件实际上仅存在于驱动器内的易失性写入缓存中。根据驱动器的不同,可能没有办法解决这个问题。
此外fsync()
,还有sync()
和syncfs()
系统调用要求系统确保所有系统范围的写入或特定文件系统上的所有写入都已命中磁盘。该实用程序sync
可用于调用这些。
然后还有O_DIRECT
to 标志open()
,它应该“尝试最小化进出该文件的 I/O 的缓存影响”。删除缓存会降低性能,因此它主要由执行自己缓存并希望对其进行控制的应用程序(数据库)使用。(O_DIRECT
并非没有问题,手册页中关于它的评论有点有趣。)
断电时会发生什么也取决于文件系统。您不仅应该关注文件数据,还应该关注文件系统元数据。如果找不到,将文件数据放在磁盘上没有多大用处。只是将文件扩展到更大的大小将需要分配新的数据块,并且需要在某处标记它们。
文件系统如何处理元数据更改以及元数据和数据写入之间的顺序差异很大。例如,ext4
如果你设置了 mount 标志data=journal
,那么所有的写入——甚至是数据写入——都会通过日志并且应该是相当安全的。这也意味着它们被写入两次,因此性能下降。默认选项尝试对写入进行排序,以便在元数据更新之前数据位于磁盘上。其他选项或其他文件系统可能更好或更糟;我什至不会尝试进行全面的研究。
实际上,在负载较轻的系统上,文件应该会在几秒钟内到达磁盘。如果您正在处理可移动存储,请在拉动媒体之前卸载文件系统以确保数据实际发送到驱动器,并且没有进一步的活动。(或者让您的 GUI 环境为您执行此操作。)
Jör*_*tag 15
有一个非常简单的方法来证明它不可能是真实的,文件的编辑始终直接保存到磁盘,即事实上,有一些文件系统没有被摆在首位磁盘的支持。如果一个文件系统不具备在首位的盘,那么它不可能更改写入到磁盘,永远。
一些例子是:
tmpfs
,一个只存在于 RAM 中的文件系统(或者更准确地说,存在于缓冲区缓存中)ramfs
,只存在于 RAM 中的文件系统sysfs
, procfs
, devfs
, shmfs
, ...)但即使对于磁盘支持的文件系统,这通常也不是真的。How To Corrupt An SQLite Database页面有一章称为同步失败,它描述了写入(在这种情况下提交到 SQLite 数据库)无法到达磁盘的许多不同方式。SQLite 也有一份白皮书,解释了为保证SQLite 中的 Atomic Commit必须跳过的许多环节。(请注意,Atomic Write比Write问题要困难得多,但当然写入磁盘是原子写入的子问题,您也可以从这篇论文中学到很多关于这个问题的知识。)这篇论文有一个关于可能出错的事情的部分,其中包括关于不完全磁盘刷新给出了一些可能阻止写入到达磁盘的微妙复杂性的示例(例如,硬盘控制器报告它已写入磁盘,而事实上它尚未写入 – 是的,有硬盘制造商这样做,根据 ATA 规范,它甚至可能是合法的,因为它在这方面的措辞含糊不清)。
Ral*_*edl 12
的确,包括 Unix、Linux 和 Windows 在内的大多数操作系统都使用写缓存来加速操作。这意味着关闭计算机而不关闭它是一个坏主意,可能会导致数据丢失。如果在准备移除 USB 存储设备之前移除它,情况也是如此。
大多数系统还提供使写入同步的选项。这意味着数据将在应用程序收到成功确认之前存储在磁盘上,代价是速度变慢。
简而言之,您应该正确关闭计算机并正确准备移除 USB 存储设备是有原因的。
它是否取决于磁盘类型(传统硬盘驱动器与固态磁盘)或我可能不知道的任何其他变量?它是仅在 Linux 中发生(如果发生)还是在其他操作系统中存在?
当您有选择时,您不应该让基于闪存的存储在没有干净关闭的情况下断电。
在像 SD 卡这样的低成本存储上,您可能会丢失整个擦除块(比 4KB 大几倍),丢失可能属于不同文件或文件系统基本结构的数据。
一些昂贵的 SSD 可能声称在断电时可以提供更好的保证。然而,第三方测试表明,许多昂贵的 SSD 无法做到这一点。为“磨损均衡”重新映射块的层是复杂且专有的。可能的故障包括驱动器上的所有数据丢失。
应用我们的测试框架,我们测试了来自六个不同供应商的 17 个商品 SSD,总共使用了超过三千个故障注入周期。我们的实验结果表明,在 17 个测试的 SSD 设备中,有 14 个在电源故障下表现出令人惊讶的故障行为,包括位损坏、剪短写入、不可序列化写入、元数据损坏和总设备故障。
2017 年:https : //dl.acm.org/citation.cfm?id=2992782&preflalayout=flat
2013 年:https : //www.usenix.org/system/files/conference/fast13/fast13-final80.pdf?wptouch_preview_theme=enabled
旋转 HDD 具有不同的特性。为了安全和简单,我建议假设它们与基于闪存的存储具有相同的实际不确定性。
除非你有具体的证据,而你显然没有。我没有旋转 HDD 的比较数据。
HDD 可能会留下一个带有错误校验和的未完全写入的扇区,这将在以后给我们一个很好的读取失败。从广义上讲,HDD 的这种故障模式完全在意料之中;本机 Linux 文件系统在设计时就考虑到了这一点。他们的目标是fsync()
在面对这种类型的断电故障时保持合同。(我们真的很想在 SSD 上看到这一点)。
但是我不确定 Linux 文件系统是否在所有情况下都能实现这一点,或者这是否可能。
此类故障后的下一次启动可能需要修复文件系统。这是Linux,有可能文件系统修复会问一些你不明白的问题,你只能按Y,希望它会自己解决。
fsync() 合约是好消息和坏消息的来源。你必须先了解好消息。
好消息:fsync()
作为写入文件数据的正确方法(例如,当您点击“保存”时)有详细记录。并且人们普遍认为,例如文本编辑器必须使用rename()
. 这是为了确保您始终保留旧文件或获取新文件(fsync()
在重命名之前已编辑)。您不想留下新文件的半写版本。
坏消息:多年来,在最流行的 Linux 文件系统上调用 fsync() 可以有效地让整个系统挂起数十秒。由于应用程序对此无能为力,因此乐观地使用 rename() 而不使用 fsync() 是很常见的,这在此文件系统上似乎相对可靠。
因此,存在未正确使用 fsync() 的应用程序。
这个文件系统的下一个版本通常避免了 fsync() 挂起 - 同时它开始依赖于 fsync() 的正确使用。
这一切都非常糟糕。许多相互冲突的内核开发人员使用的不屑一顾的语气和谩骂可能无助于理解这段历史。
当前的解决方案是当前最流行的 Linux 文件系统 默认支持 rename() 模式而不需要 fsync()与之前的版本实现“bug-for-bug 兼容性”。这可以通过 mount 选项禁用noauto_da_alloc
。
这不是一个完整的保护。基本上它在 rename() 时间刷新挂起的 IO,但它不会在重命名之前等待 IO 完成。不过,这比例如 60 秒的危险窗口要好得多!另请参阅使用 rename() 替换现有文件时哪些文件系统需要 fsync() 以确保崩溃安全的答案?
一些不太流行的文件系统不提供保护。XFS拒绝这样做。而且UBIFS也没有实施,显然可以接受,但需要大量工作才能实现。同一页面指出 UBIFS 在数据完整性方面还有其他几个“待办事项”问题,包括断电问题。UBIFS 是一种直接用于闪存的文件系统。我想 UBIFS 提到的闪存存储的一些困难可能与 SSD 错误有关。
在负载较轻的系统上,内核会让新写入的文件数据在页面缓存中停留 30 秒write()
,然后再将其刷新到磁盘,以针对很快被删除或再次修改的情况进行优化。
Linux 的dirty_expire_centisecs
默认值为 3000(30 秒),并控制新写入的数据“过期”多长时间。(见https://lwn.net/Articles/322823/)。
有关更多相关可调参数,请参阅https://www.kernel.org/doc/Documentation/sysctl/vm.txt,有关更多信息,请参阅google。(例如谷歌上dirty_writeback_centisecs
)。
Linux 默认为/proc/sys/vm/dirty_writeback_centisecs
500(5 秒),PowerTop 建议将其设置为 1500(15 秒)以减少功耗。
在开始将文件写入磁盘之前,延迟回写还使内核有时间查看文件有多大。具有延迟分配的文件系统(例如 XFS,以及现在可能的其他文件系统)甚至不选择将新写入的文件数据放置在磁盘上的哪个位置,除非需要,这与为 inode 本身分配空间分开。例如,这可以通过让它们避免将大文件的开头放在其他文件之间的 1 兆间隙中来减少碎片。
如果正在写入大量数据,则可以通过页面缓存中可以有多少脏(尚未同步到磁盘)数据的阈值触发写回磁盘。
但是,如果您没有做太多其他事情,那么在点击小文件保存后,您的硬盘驱动器活动指示灯将不会亮起 5(或 15)秒。
如果您的编辑器fsync()
在写入文件后使用,内核会立即将其写入磁盘。(并且fsync
在数据实际发送到磁盘之前不会返回)。
磁盘内的写入缓存也可能是一回事,但与 Linux 的页面缓存算法不同,磁盘通常会尝试尽快将其写入缓存提交到永久存储。磁盘写入缓存更像是一个存储缓冲区,用于吸收少量的写入,但也可能延迟写入以支持读取,并为磁盘固件提供空间以优化查找模式(例如,执行两个附近的写入或读取而不是执行一个,然后远寻,又寻回。)
在旋转(磁性)磁盘上,如果在您的写入之前有待处理的读/写,您可能会看到一些 7 到 10 毫秒的寻道延迟,然后来自 SATA 写入命令的数据实际上可以安全地关闭电源。(关于这个问题的其他一些答案更详细地介绍了磁盘写入缓存和日志文件系统可以用来避免损坏的写入障碍。)