Linux中的文件编辑是否直接保存到磁盘?


57

我曾经认为文件更改直接保存到磁盘中,也就是说,一旦我关闭文件并决定单击/选择“保存”。但是,在最近的一次对话中,我的一个朋友告诉我,通常情况并非如此。操作系统(特别是我们在谈论Linux系统的操作系统)将更改保存在内存中,并且它具有一个守护程序,该守护程序实际上将内容从内存写入磁盘。

他甚至给出了外部闪存驱动器的示例:这些驱动器已安装到系统中(已复制到内存中),有时由于后台驻留程序尚未将内容保存到闪存中而导致数据丢失。这就是为什么我们卸下闪存驱动器。

我不了解操作系统的功能,因此我完全不知道这是否正确以及在哪种情况下。我的主要问题是:这是否会像Linux / Unix系统(可能还有其他OS)中所述发生?例如,这是否意味着如果我在编辑和保存文件后立即关闭计算机,则更改很可能会丢失吗?也许这取决于磁盘类型-传统硬盘驱动器还是固态磁盘?

该问题专门针对具有磁盘来存储信息的文件系统,即使人们进行了任何澄清或比较也是如此。


8
粮农组织:关闭投票队列审阅者。这不是学习材料的要求。参见unix.meta.stackexchange.com/q/3892/22812
Anthony Geoghegan,

2
高速缓存对于用户而言是不透明的,在最佳情况下,您必须这样做sync,并且应用程序必须flush确保回写高速缓存,但是即使成功sync完成也不能保证仅将内核高速缓存刷新到磁盘上才能写回物理磁盘,这可能会有延迟在驱动程序或磁盘硬件中(例如,丢失的驱动器缓存)
crasic '18

1
虽然我不同意这是学习材料的要求,但我确实认为这个问题在目前的形式上有些宽泛。将范围限制为Linux发行版(或任何特定的OS),并可能将其限制为某些存储技术和文件系统。
杰夫·谢勒

3
正如@AnthonyGeoghegan指出的那样,我不认为这个问题是学习材料的要求。我认为这很具体;我并没有要求对Linux文件系统进行详尽而深入的解释或使用手册;只是我想弄清楚的一个简短想法。
JuanRocamonde

3
的确,@ JeffSchaller可能有点宽泛。我将尝试对其进行一些编辑。但是,老实说,如果该站点不是针对此类问题的,而是直接针对Linux功能的,那么它的目的是什么?
JuanRocamonde

Answers:


70

如果我在编辑和保存文件后立即关闭计算机,则更改很可能会丢失吗?

他们可能是。我不会说“最有可能”,但是可能性取决于很多事情。


一种提高文件写入性能的简单方法是,操作系统仅缓存数据,告知(说谎)应用程序写入经过的内容,然后稍后再进行实际写入。如果同时有其他磁盘活动在进行,这将特别有用:操作系统可以确定读取的优先级,并在以后进行写入。它还可以完全消除实际写入的需要,例如在之后快速删除临时文件的情况下。

如果存储速度较慢,则缓存问题会更加明显。将文件从快速的SSD复制到速度较慢的USB记忆棒可能会涉及大量写缓存,因为USB记忆棒无法跟上。但是您的cp命令返回得更快,因此您可以继续工作,甚至可以编辑刚刚复制的文件。


当然,这样的缓存有一个缺点,您可能会在实际保存某些数据之前就将其丢失。如果用户的编辑器告诉他们写入成功,但文件实际上不在磁盘上,那么用户会感到沮丧。这就是为什么要进行fsync()系统调用的原因,该调用仅在文件实际到达磁盘后才返回。在向用户报告写入成功之前,编辑器可以使用它来确保数据正常。

我说“应该”,因为驱动器本身可能会向OS发出相同的谎言,并说写入已完成,而文件实际上仅存在于驱动器中的易失性写缓存中。根据驱动器的不同,可能无法解决。

除之外fsync(),还有sync()syncfs()系统调用,它们要求系统确保所有系统范围的写入或特定文件系统上的所有写入均已命中磁盘。该实用程序sync可用于调用那些。

然后还有O_DIRECTtoopen()标志,该标志应“试图最大程度地减少往返于此文件的I / O的缓存影响”。删除缓存会降低性能,因此大多数由自己进行缓存并希望对其进行控制的应用程序(数据库)使用。(O_DIRECT并非没有问题,手册页中对此的评论有些有趣。)


断电时发生的情况还取决于文件系统。您不仅应该关注文件数据,还应该关注文件系统元数据。如果找不到,将文件数据存储在磁盘上并没有多大用处。仅仅将文件扩展到更大的大小将需要分配新的数据块,并且需要将它们标记在某个位置。

文件系统如何处理元数据更改以及元数据和数据写入之间的顺序差异很大。例如,使用ext4,如果您设置了mount标志data=journal,则所有写入-甚至数据写入-都会经过日志,因此应该相当安全。这也意味着它们被写入两次,因此性能下降。默认选项尝试对写入进行排序,以便在更新元数据之前将数据保存在磁盘上。其他选项或其他文件系统可能会更好或更坏;我什至不会尝试全面学习。


实际上,在负载较轻的系统上,文件应在几秒钟内命中磁盘。如果要处理可移动存储,请在拉出介质之前先卸载文件系统,以确保数据已实际发送到驱动器,并且没有进一步的活动。(或者让您的GUI环境为您执行此操作。)


您的some cases where链接似乎没有提到任何此类情况-而是说当应用程序使用时存在问题fsync。还是应该查看评论以找到您要指出的这些情况?
罗斯兰

1
您还可以sync直接用作系统外壳程序命令来拨动内核以刷新所有缓存。
crasic

3
实际上,在负载较轻的系统上,文件将在片刻之内击中磁盘。 仅当编辑器fsync()在写入文件后使用时。Linux的默认/proc/sys/vm/dirty_writeback_centisecs值为500(5秒),PowerTop建议将其设置为1500(15秒)。(kernel.org/doc/Documentation/sysctl/vm.txt)。在负载较轻的系统上,内核会write()在刷新到磁盘之前很久才让它放在页面缓存中,以优化其很快被删除或修改的情况。
彼得·科德斯

2
+1表示,因为驱动器本身对OS也可能具有相同的含义。我的理解是,执行此类缓存的驱动器也具有足够的功率电容,即使在灾难性的功率损耗时也可以保存其缓存。这不是特定于操作系统的。Windows具有“安全删除USB”机制,可以在用户拔出插头之前执行缓存刷新。
studog

1
@studog,我不确定,尤其是在消费类硬件上。但这可能只是妄想症。但是,进行测试将很有趣。
ilkkachu

14

有一种极其简单的方法来证明将文件编辑总是直接保存到磁盘是正确的,也就是说,存在某些文件系统最初没有被磁盘支持的事实。如果一个文件系统不具备在首位的盘,那么它不可能更改写入到磁盘,永远

一些例子是:

  • tmpfs,仅存在于RAM(或更确切地说,位于缓冲区高速缓存中)的文件系统
  • ramfs,仅存在于RAM中的文件系统
  • 任何网络文件系统(NFS,CIFS / SMB,AFS,AFP等)
  • 任何虚拟文件系统(sysfsprocfsdevfsshmfs,...)

但是,即使对于磁盘支持的文件系统,这通常也不正确。如何损坏SQLite数据库页面上有一章称为同步失败,该章描述了写入(在这种情况下是提交到SQLite数据库)可能无法到达磁盘的许多不同方式。SQLite也有一份白皮书,说明了为确保SQLite中的原子提交而必须跳过的许多难题。(请注意,原子写入要比问题要难得多,但是向磁盘写入当然是原子写入的子问题,您也可以从本文中学到很多有关该问题的知识。)可能出错的部分,其中包含有关不完整的磁盘刷新提供了一些微妙的复杂示例,这些示例可能会阻止写入到达磁盘(例如,HDD控制器报告事实表明磁盘尚未写入磁盘时,它已将其写入磁盘–是的,有些HDD制造商正在这样做,并且根据ATA规范甚至可能是合法的,因为在此方面措辞含糊。


10
答案的第一部分只是对所使用的确切字词be之以鼻。除了嘲笑用户之外,我看不到它有什么作用。显然,网络文件系统不会写入本地磁盘,但问题仍然存在。
管道

3
正如@pipe指出的那样,有些文件系统因为不使用磁盘存储数据而没有将数据保存到磁盘中,这一事实并不能决定拥有这些文件系统的人是否可以直接保存它。但是,答案看起来很有趣
JuanRocamonde

1
@pipe我很确定使用术语“ besserwissering”是besserwissering!作为具有权威的德国Besserwisser说。
Volker Siegel '18

11

的确,包括Unix,Linux和Windows在内的大多数操作系统都使用写缓存来加快操作速度。这意味着关闭计算机而不关闭计算机是一个坏主意,并可能导致数据丢失。如果在准备删除USB存储设备之前将其删除,则情况也是如此。

大多数系统还提供使写入同步的选项。这意味着数据将在应用程序收到成功确认之前存储在磁盘上,但速度较慢。

简而言之,为什么要正确关闭计算机并准备好要卸下的USB存储设备是有原因的。


谢谢您的回复!有没有办法在Linux中强制对特定文件进行磁盘写入?也许链接到教程或文档页面,甚至SE问题也没问题:)
JuanRocamonde

4
您可以fsync()通过程序中的syscall 强制写入文件。从shell,只需使用sync命令。
RalfFriedl

2
在某些版本的Linux中有(或至少有)一些文件系统,这些文件系统sync是作为无操作实现的。而即使对于文件系统正确地执行sync,仍存在一些磁盘固件执行问题FLUSH CACHE作为一个无操作或立即从它返回,并在后台执行它。
约尔格W¯¯米塔格

9

1.基于闪存的存储

它取决于磁盘类型(传统硬盘驱动器还是固态磁盘)或其他我可能不知道的变量?它是否仅在Linux中发生(如果确实如此)或在其他OS中存在?

当您选择时,如果不进行彻底关机,则不应允许基于闪存的存储掉电。

在像SD卡这样的低成本存储设备上,您可能会丢失整个擦除块(比4KB大几倍),并丢失可能属于不同文件或文件系统基本结构的数据。

面对电源故障,一些昂贵的SSD可能会提供更好的保证。但是,第三方测试表明,许多昂贵的SSD无法做到这一点。重新映射块以进行“磨损平衡”的层是复杂且专有的。可能的故障包括驱动器上所有数据的丢失。

应用我们的测试框架,我们总共使用了三千多个故障注入周期来测试来自六个不同供应商的17个商用SSD。我们的实验结果表明,在17个经过测试的SSD设备中,有14个在电源故障下表现出令人惊讶的故障行为,包括位损坏,shorn写入,不可序列化的写入,元数据损坏以及整个设备故障。

2017年:https : //dl.acm.org/citation.cfm?id=2992782&preflayout=flat

2013:https//www.usenix.org/system/files/conference/fast13/fast13-final80.pdf?wptouch_preview_theme = enabled

2.旋转硬盘驱动器

旋转硬盘具有不同的特性。为了安全和简单起见,我建议假定它们具有与基于闪存的存储相同的实际不确定性。

除非您有具体的证据,否则您显然没有。我没有旋转硬盘的比较数据。

HDD可能会使一个不完整写入的扇区具有错误的校验和,这将在以后给我们带来很好的读取失败。从广义上讲,HDD的这种故障模式是完全可以预期的。在设计本机Linux文件系统时要牢记这一点。他们旨在fsync()在面对此类断电故障时维护合同。(我们真的很想在SSD上看到这一保证)。

但是,我不确定Linux文件系统是否在所有情况下都能实现这一目标,或者是否有可能实现。

此类故障后的下一次引导可能需要修复文件系统。在Linux上,文件系统修复可能会问一些您不理解的问题,您只能在其中按Y并希望它能自行解决。

2.1如果您不知道什么是fsync()合同

fsync()合同是好消息和坏消息的来源。您必须首先了解好消息。

好消息:fsync()有据可查,是写文件数据的正确方法,例如,当您单击“保存”时。众所周知,例如,文本编辑器必须使用原子替换现有文件rename()。这是为了确保您始终保留旧文件或获取新文件(fsync()在重命名之前编辑的文件)。您不想留下新文件的半写版本。

坏消息:多年来,在最受欢迎的Linux文件系统上调用fsync()可能会使整个系统挂起数十秒钟。由于应用程序对此无能为力,因此在没有fsync()的情况下乐观地使用Ruiname()是很常见的,这在此文件系统上似乎是相对可靠的。

因此,存在不正确使用fsync()的应用程序。

此文件系统的下一个版本通常避免fsync()挂起-在它开始依赖正确使用fsync()的同时。

这一切都非常糟糕。许多相互矛盾的内核开发人员所使用的轻蔑的语气和煽动性可能无法帮助理解这一历史。

当前的解决方案是,当前最受欢迎的Linux文件系统 默认情况下支持重命名()模式,而无需fsync()与以前的版本实现“ bug兼容”。可以使用mount选项禁用它noauto_da_alloc

这不是一个完整的保护。基本上,它在rename()时刷新待处理的IO,但它不等待IO完成重命名。但是,这比60秒危险窗口要好得多!另请参见答案,当用named()替换现有文件时哪些文件系统需要fsync()来确保崩溃安全?

一些不太流行的文件系统不提供保护。XFS 拒绝这样做。而且,UBIFS也没有实现它,显然它可以接受,但需要大量工作才能使其成为可能。同一页还指出,UBIFS还有其他几个“ TODO”问题,涉及数据完整性,包括断电问题。UBIFS是直接在闪存上使用的文件系统。我想象UBIFS提到的闪存存储的某些困难可能与SSD错误有关。


5

在负载较轻的系统上,内核将在a之后将新写入的文件数据放在页面高速缓存中大约30秒钟write(),然后再将其刷新到磁盘,以针对即将被再次删除或修改的情况进行优化。

Linux的dirty_expire_centisecs默认值为3000(30秒),并控制新写入的数据“过期”多长时间。(请参阅https://lwn.net/Articles/322823/)。

有关更多相关的可调参数,请参见https://www.kernel.org/doc/Documentation/sysctl/vm.txt;有关更多信息,请参见google。(例如上的google dirty_writeback_centisecs)。

Linux的默认/proc/sys/vm/dirty_writeback_centisecs值为500(5秒),PowerTop建议将其设置为1500(15秒)以减少功耗。


延迟的回写操作还使内核有时间在开始将文件写入磁盘之前,先查看文件的大小。分配延迟的文件系统(例如XFS,以及如今可能的其他文件系统)甚至没有为索引节点本身分配空间,直到必要时才选择将磁盘上放置新写入文件数据的位置。例如,通过让它们避免将大文件的开头放在其他文件之间1兆的间隙中,可以减少碎片。

如果要写入大量数据,则可以通过页面缓存中可以包含多少脏数据(尚未同步到磁盘)的阈值来触发写回磁盘。

但是,如果您没有执行其他任何操作,则在单击保存在一个小文件上后,硬盘驱动器活动指示灯将不亮5(或15)秒。


如果您的编辑器fsync()在写入文件后使用,内核将立即将其写入磁盘。(并且fsync只有在数据实际发送到磁盘后才会返回)。


磁盘的写缓存也可能是一回事,但是磁盘通常会尝试尽快将其写缓存提交到永久存储,这与Linux的页面缓存算法不同。磁盘写缓存更像是一种存储缓冲区,可以吸收少量突发写入,但也可以延迟写入以支持读取,并为磁盘固件腾出空间来优化查找模式(例如,执行两次附近的写入或读取而不是进行一次写入) ,然后搜索较远,然后返回。)

在旋转的(磁性)磁盘上,如果在写之前有未决的读/写操作,则从SATA写入命令获得的数据实际上可以安全地断电之前,您可能会看到每个7到10毫秒的寻道延迟。(有关此问题的其他一些答案将更详细地介绍磁盘写入缓存和日记化的FS可用于避免损坏的写入障碍。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.