2010-09-21 25 views
14

什么是在POSIX文件系统中持久重命名文件的正确方法?特别想知道目录下的fsyncs。 (如果这取决于OS/FS,我在问Linux和ext3/ext4)。如何在POSIX中持久地重命名文件?

注意:有StackOverflow上约耐用改名等问题,但AFAICT它们没有解决FSYNC-ING的目录(这是最重要的,我 - 我还没有修改文件数据)。

我现在有(在Python):

dstdirfd = open(dstdirpath, O_DIRECTORY|O_RDONLY) 
rename(srcdirpath + '/' + filename, dstdirpath + '/' + filename) 
fsync(dstdirfd) 

具体问题

  • 这是否也隐含FSYNC源目录?或者我可能会在电源循环后显示两个目录中的文件(意味着我必须检查硬链接数并手动执行恢复),即不可能保证持久的原子移动操作?
  • 如果我fsync源目录而不是目标目录,那也隐含fsync目的地目录?
  • 是否有任何有用的相关测试/调试/学习工具(故障注入器,内省工具,模拟文件系统等)?

在此先感谢。

回答

14

POSIX定义了重命名功能必须是原子:

http://pubs.opengroup.org/onlinepubs/009695399/functions/rename.html

因此,如果您重命名( A,B),在任何情况下你都不应该看到两个目录中的文件或两个目录都没有文件。不管你用fsync()做什么或者系统是否崩溃,总会有一个。

但这并不能解决确保rename()操作持久的问题。 POSIX回答了这个问题:

如果_POSIX_SYNCHRONIZED_IO定义,在FSYNC()函数应强制与文件描述符fildes指定的同步I/O完成状态的文件相关联的所有当前排队的I/O操作。所有的I/O操作应按照同步I/O文件完整性完成的定义完成。

(从http://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html

所以,如果你的fsync()的目录,等待重命名操作必须在这个时间返回传输到磁盘上。任何一个目录的fsync()都应该是足够的,因为rename()操作的原子性会要求这两个目录的变化以原子方式进行。

最后,相对于在另一个答复中提到的博客文章的要求,这样做的原因解释如下:

的FSYNC()函数是为了迫使从数据的物理写入缓冲区缓存,并确保在系统崩溃或其他故障后,直到fsync()调用之前的所有数据都记录在磁盘上。由于这里没有定义“缓冲区缓存”,“系统崩溃”,“物理写入”和“非易失性存储”的概念,因此措辞必须更加抽象。

声称符合POSIX标准,并且考虑的系统,它正确的行为(即不是一个错误或硬件故障)来完成的FSYNC(),并不会持续整个系统崩溃这些变化将不得不刻意歪曲本身就规格而言。

(更新带有附加信息的回复:Linux特有的与便携行为)

+1

这里的推理是非常错误的。 - 举例来说,rename()的“atomicity”是指“新路径”,在该路径下应该是旧文件(如果有的话)或重命名的文件,其间没有状态(如其他进程所见) 。 – 2013-05-11 19:02:22

+0

Robert指出,在将新的inode分配给名称方面,rename为“atomic”,新的inode是否指向未刷新的数据不是由POSIX定义的。 – ArekBulski 2015-09-19 18:14:38

-1

您的问题的答案将取决于所使用的特定操作系统,正在使用的文件系统的类型以及源和目标是否在同一设备上。

我首先阅读您正在使用的平台上的重命名(2)手册页。

+0

已经咨询过那个手册页 - 没有关系。你是说没有可移植的方式来重新命名目录?我愿意相信,但对一个更清晰的声明和理想的支持证据感兴趣。另外,你是否知道最近Linux 2.6的ext3/4的答案(因为这个问题被标记了 - 只更新了主要文本)? – Yang 2011-04-13 05:04:21

+0

好吧,如果你只关心linux和ext3/4,那么简单一点。需要注意的是,linux重命名(2)手册页提到:'但是,覆盖时,可能会出现一个窗口,其中oldpath和newpath指的是正在重命名的文件。' – 2011-05-10 20:40:18

-3

这听起来像你想要做的文件系统的工作。如果你移动一个文件,内核和文件系统负责原子操作和故障恢复,而不是你的代码。

不管怎么说,这篇文章似乎为解决有关FSYNC您的问题: http://blogs.gnome.org/alexl/2009/03/16/ext4-vs-fsync-my-take/

+0

阅读该帖子之前,它是众多那把我带到了这里。它特别指出:“在写入后不久系统崩溃的情况下,我们获取新文件的可能性比旧文件更大(为了获得最大可能性,您还需要fsync文件所在的目录)”。我的问题是关于在跨目录重命名时会发生什么。 – Yang 2011-04-15 04:07:50

11

不幸的是戴夫的答案是错的。

并非所有的POSIX系统甚至可能拥有持久存储。如果他们这样做,在系统崩溃后它仍然是“允许的”。对于那些系统来说,no-op fsync()是有意义的,并且这样的fsync()在POSIX下明确允许。在旧目录,新目录,两者或任何其他位置恢复文件也是合法的。 POSIX不保证系统崩溃或文件系统恢复。

真正的问题应该是:

如何做哪些支持通过POSIX API系统持久重命名?

你需要做的fsync()两个,源目标目录,因为最小的FSYNC()s的应该做的是坚持源或目标目录应该是什么样子等。

fsync(destdirfd)是否也隐式地fsync源目录?

  • POSIX一般:没有,没有什么暗示
  • EXT3/4:我不知道,如果都改变为源和目标目录在杂志同一事务结束。如果他们这样做,他们会一起承诺。

或者我可能会在电源循环(“崩溃”)后在两个目录中显示文件,即无法保证持久的原子移动操作?

  • POSIX一般:没有任何保证,但是你应该FSYNC()两个目录,这可能不是原子耐用
  • EXT3/4:您最低限度有多大的fsync()需要依赖于挂载选项。例如。如果使用“dirsync”安装,则不需要这两个fsync()中的任何一个。最多你需要两个fsync(),但我几乎可以肯定一个就足够了(然后是atomic-durable)。

如果我fsync源目录,而不是目的地目录,那也隐含fsync目的地目录?

  • POSIX:没有
  • EXT3/4:我真的相信在同一事务都结束了,所以它并不重要的人,你的fsync()
  • 旧内核的ext3:(如果他们不在同一个事务中)一些不太理想的实现在fsync()上做了太多的同步,我敢打赌它确实提交了之前发生的每一个事务。是的,一个正常的实现将首先将其链接到目标,然后将其从源代码中移除。所以fsync(srcdirfd)也会触发目标的fsync()。
  • EXT4/ext3的最新:如果他们不是在同一个交易,你可能能够完全同步他们独立(这样做既)

是否有任何有用的相关测试/调试/学习工具(故障注入器,内省工具,模拟文件系统等)?

对于真正的碰撞,没有。顺便说一下,真正的崩溃超出了内核的观点。硬件可能会重新排序写入(并且无法写入所有内容),从而破坏文件系统。 Ext4对此做了更好的准备,因为它默认启用了写入挂载(挂载选项)(ext3不),并且可以使用日志校验和(也是挂载选项)检测损坏。

而对于学习:找出这两个变化是否以某种方式在期刊中链接! :-P

+1

这是一个微妙的观点,但我并不是争辩说一个目录的fsync()意味着另一个目录的* fsync()。它的fsync()与rename()的原子性相结合,要求*到另一个目录的特定更改*也要同步到磁盘。我可以相信事实并非如此,但这是我对规范的阅读。你是否有一个参考文件来支持你的解释:即使“原子”变化的一部分是fsync'd,原子性在崩溃中也不能保证? – 2013-08-30 18:05:19

+0

是的,我的确:你的链接到[fsync()on opengroup.org](http://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html)。 “它明确打算允许null实现。”那就是:没有保证。 - 和rename()和fsync()的如意连接是你的发明。不,这不是一个微妙的问题。 – 2013-09-03 17:03:32

+1

一致认为,POSIX系统不需要做任何这样的更改持久,而真正的问题是如何使用API​​在支持它的系统上进行持久更改。 (这看起来很迂腐,因为这个问题显然预示着底层系统支持它。)在要求参考时,我指的是你的说法,即使在* do *支持对磁盘进行同步更改的系统上,一个成功的任何一个目录的fsync()都可能导致文件在崩溃后显示在两个地方(或者都不显示)。 – 2013-09-03 20:58:16