Linux上的原子写入文件

21

有没有一种方法可以将缓冲区原子地转储到文件中?

所谓“原子性”,是指:例如在写入过程中某人终止了我的应用程序时,我希望文件处于写入之前或之后的状态,而不是处于损坏的中间状态。

如果答案是“否”,那么可能可以使用非常小的缓冲区来完成吗? 例如,我是否可以使用单个8字节fwrite(在x64平台上),将两个连续的int32_t变量转储,并确保这两个int32被转储,或者二者都未被转储,而不仅仅是其中一个?


17
通常的解决方法是在文件的副本上进行操作,在最后一刻原子地交换它们。 - Marc Glisse
4
同意@MarcGlisse的观点,预防文件损坏的唯一可靠方法是编写一个新文件,并将其作为重命名操作完成后移动到旧文件位置,以此来保证原子性。 - Philip Couling
5
我正准备像 @MarcGlisse 一样提出同样的建议。但请注意,如果源文件和目标文件在同一个文件系统上,则移动(即重命名)文件才是原子操作。 - 5gon12eder
3
如果文件不在同一文件系统上,就无法在C或C++中重命名文件。有关更多信息,请参阅http://www.gnu.org/software/libc/manual/html_node/Renaming-Files.html。 - Philip Couling
非常感谢你的回答!我会考虑重命名建议。在我的情况下,捕获信号似乎很棘手,因为我同时使用了相当多的文件,并且需要添加一些附加的同步。对每个文件执行相同的过程,以使它们保持良好状态,更适合我。顺便说一句,很高兴知道重命名是原子性的! - mambo_sun
显示剩余2条评论
1个回答

23

我建议先写入临时文件,然后再对其进行rename(2)操作。

ofstream o("file.tmp"); //Write to a temporary file
o << "my data";
o.close();

//Perform an atomic move operation... needed so readers can't open a partially written file
rename("file.tmp", "file.real");

5
这是正确的方法。在POSIX系统上,一旦打开文件描述符,其i节点保证存在,直到文件描述符被销毁。因此,无论写入什么内容,旧文件的读取者都能看到相同的数据。重命名操作在同一文件系统上保证是原子性的,因此,这个惯用语使读写操作并发安全。 - Jazzwave06
有没有办法在不同目录中使用多个文件进行操作?比如说我有a/foo.txt和b/c/bar.txt,然后我创建了foo.tmp和bar.tmp;我该如何确保两个真实的文件同时被原子地更新? - michaelsnowden
5
这还不够。在重命名文件之前,你必须使用fsync同步文件。否则,如果出现断电情况,你可能会得到一个空文件。 - Emil
1
@Emil 取决于你的需求。如果你需要容错性并且文件需要存储在磁盘上,那么是的,你需要 fsync。但如果你只是将文件存储在 tmpfs 中,则不需要。 - Gillespie
如果用户没有目录中文件的写入权限,则此方法无法使用。即使用户可以对该文件进行写入,但是他们可能无法创建一个新文件,而这恰好是rename/mv所做的操作。因此,我们在这里没有完整的答案。 - CR.
在你重命名文件之后,你还需要对文件夹进行fsync操作。 - Vad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接