Directory 如何在POSIX中持久地重命名文件?

Directory 如何在POSIX中持久地重命名文件?,directory,posix,rename,ext4,fsync,Directory,Posix,Rename,Ext4,Fsync,在POSIX文件系统中持久重命名文件的正确方法是什么?特别想知道目录上的fsync。(如果这取决于OS/FS,我会询问Linux和ext3/ext4) 注意:关于持久重命名的StackOverflow还有其他问题,但它们没有解决同步目录的问题(这对我来说很重要-我甚至没有修改文件数据) 我目前有(Python): 具体问题: 这是否也隐式地fsync源目录?或者,在一次断电后,文件是否会显示在两个目录中(这意味着我必须检查硬链接计数并手动执行恢复),也就是说,不可能保证持久的原子移动操作 如果

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

注意:关于持久重命名的StackOverflow还有其他问题,但它们没有解决同步目录的问题(这对我来说很重要-我甚至没有修改文件数据)

我目前有(Python):

具体问题

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

提前感谢。

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


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

我觉得您好像在尝试完成文件系统的工作。如果移动文件,内核和文件系统负责原子操作和故障恢复,而不是代码

无论如何,本文似乎解决了您关于fsync的问题: POSIX定义了

所以,如果重命名(A,B),在任何情况下都不应该在两个目录或两个目录中都看到文件的状态。无论您使用fsync()做什么,或者系统是否崩溃,总有一个

但这并不能解决确保rename()操作持久性的问题

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

因此,如果在目录中使用fsync(),则在返回时,挂起的重命名操作必须传输到磁盘。任何一个目录的fsync()都应该足够,因为rename()操作的原子性要求两个目录的更改以原子方式同步

最后,与另一个答案中提到的博客帖子中的说法相反,其理由解释如下:

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

如果一个系统声称是POSIX兼容的,并且认为完成fsync()的行为是正确的(即不是错误或硬件故障),并且在系统崩溃时不保留这些更改,那么该系统就必须故意歪曲规范


(更新了关于Linux特定与便携行为的附加信息)

不幸的是,Dave的答案是错误的。

并非所有POSIX系统都有持久的存储。如果他们这样做了,在系统崩溃后仍然“允许”对其进行冲洗。对于那些系统来说,无操作fsync()是有意义的,并且在POSIX下明确允许这样的fsync()。文件可以在旧目录、新目录、两者或任何其他位置恢复也是合法的。POSIX不保证系统崩溃或文件系统恢复

真正的问题应该是:

如何在通过POSIX API支持持久重命名的系统上进行持久重命名?

您需要在源目录和目标目录上都执行fsync(),因为这些fsync()应该执行的最小操作是持久化源目录或目标目录的外观

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

  • POSIX:不,这并不意味着
  • ext3/4:我不确定对源目录和目标目录的更改是否都会在日志中的同一事务中结束。如果他们这样做了,他们会一起承诺
或者,在一次断电(“崩溃”)后,文件是否会出现在两个目录中,即无法保证持久的原子移动操作?

  • POSIX概述:没有保证,但您应该将两个目录都设置为fsync(),这可能不是原子持久的
  • ext3/4:最少需要多少fsync()取决于装载选项。例如,如果安装了“dirsync”,则不需要这两个fsync()中的任何一个。最多您需要两个fsync(),但我几乎可以肯定一个就足够了(原子持久)
如果我对源目录而不是目标目录进行fsync,这是否也会隐式地对目标目录进行fsync?

  • POSIX:没有
  • ext3/4:我真的相信这两个都会在同一个事务中结束,所以您选择其中哪一个fsync()并不重要
  • 较旧的内核ext3:(如果它们不在同一事务中)一些不太理想的实现确实在fsync()上进行了太多的同步,我打赌它确实提交了之前的每个事务。是的,一个普通的实现会首先将它链接到目标,然后将它从源中删除。因此,fsync(srcdirfd)也会触发目标的fsync()
  • ext4/latest ext3:如果它们不在同一个tran中
    dstdirfd = open(dstdirpath, O_DIRECTORY|O_RDONLY)
    rename(srcdirpath + '/' + filename, dstdirpath + '/' + filename)
    fsync(dstdirfd)