【问题标题】:How to durably rename a file in POSIX?如何在 POSIX 中持久地重命名文件?
【发布时间】:2011-04-15 10:56:40
【问题描述】:

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

注意:StackOverflow 上还有其他关于持久重命名的问题,但 AFAICT 他们没有解决对目录进行 fsync 的问题(这对我来说很重要 - 我什至没有修改文件数据)。

我目前有(在 Python 中):

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

具体问题

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

提前致谢。

【问题讨论】:

    标签: directory posix rename ext4 fsync


    【解决方案1】:

    很遗憾,戴夫的回答是错误的。

    并非所有 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/最新的 ext3:如果它们不在同一个事务中,您也许可以完全独立地同步它们(两者都这样做)

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

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

    为了学习:找出这两个变化是否在日志中以某种方式联系起来! :-P

    【讨论】:

    • 这是一个微妙的点,但我并不是说一个目录的 fsync() 暗示另一个目录的 fsync()。正是 fsync() 与 rename() 的原子性相结合,要求对另一个目录的 特定更改 也同步到磁盘。我可以相信情况并非如此,但这是我对规范的阅读。您是否有参考来支持您的解释,即即使“原子”更改的一部分是 fsync 的,也不能保证崩溃时的原子性?
    • 是的,我愿意:您指向fsync() on opengroup.org 的链接。 “明确表示允许空实现。”那就是:没有保证。 – rename() 和 fsync() 的如意链接是你的发明。不,这不是一个微妙的点。
    • 同意 POSIX 系统不需要使任何此类更改持久化,真正的问题是如何使用 API 使更改在支持它的系统上持久化。 (不过,这似乎很迂腐,因为这个问题显然预设了底层系统支持它。)在要求参考时,我指的是您声称即使在 确实 支持同步更改的系统上到磁盘,任一目录的成功 fsync() 仍然可以导致文件在崩溃后显示在两个位置(或都不显示)。
    • POSIX 文档既没有讨论任何类别或级别的碰撞安全也不如何达到这些级别。 - 我很难指出他们不谈论这个的具体地方。 – 我挑战你把我指向他们做的地方...... – 另一方面:我不知道任何 Linux ext3/ext4 文档声称它支持任何类型的崩溃安全 rename() “POSIX大大地”。甚至没有一种可移植的方式来确定系统是否支持这样的事情。
    • Fsync 甚至不能保证将缓冲区刷新到磁盘。如果文件系统可以通过其他方式保证安全,则 POSIX 明确允许无操作实现。
    【解决方案2】:

    POSIX 定义 rename function must be atomic

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

    但这并不能解决确保 rename() 操作持久的问题。 POSIX answers this question:

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

    所以如果你 fsync() 一个目录,挂起的重命名操作必须在它返回时传输到磁盘。任一目录的 fsync() 应该就足够了,因为 rename() 操作的原子性要求两个目录的更改原子同步。

    最后,与另一个答案中提到的博客文章中的主张相反,这样做的理由如下:

    fsync() 函数旨在强制从缓冲区缓存中物理写入数据,并确保在系统崩溃或其他故障后,直到 fsync() 调用时间的所有数据都记录在磁盘。由于这里没有定义“缓冲缓存”、“系统崩溃”、“物理写入”和“非易失性存储”的概念,所以措辞要抽象一些。

    声称符合 POSIX 并认为完成 fsync() 并且不会在系统崩溃期间保留这些更改的正确行为(即不是错误或硬件故障)的系统必须故意歪曲自己的尊重符合规范。

    (更新了更多信息:Linux 特定与可移植行为)

    【讨论】:

    • 这里的推理是非常错误的。 – rename() 的“原子性”,例如,指的是“新路径”,在新路径下应该是旧文件(如果有的话)或重命名的文件,中间没有状态(如其他进程所见) .
    • Robert 指出,对于将新 inode 分配给 name,rename 是“原子的”,新 inode 是否指向未刷新的数据不是 POSIX 定义的。
    【解决方案3】:

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

    我会先阅读您正在使用的平台上的 rename(2) 手册页。

    【讨论】:

    • 已经查阅过该手册页 - 没有任何相关性。您是说没有可移植的方式来跨目录重命名?我愿意相信这一点,但对更清晰的陈述和理想的支持证据感兴趣。另外,你知道最近 Linux 2.6 的 ext3/4 的答案吗(因为这个问题被标记了 - 刚刚更新了正文)?
    • 啊,好吧,如果你只关心 linux 和 ext3/4,那就更简单了。 linux rename(2) 手册页提到的一个警告是:However, when overwriting there will probably be a window in which both oldpath and newpath refer to the file being renamed.
    【解决方案4】:

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

    无论如何,这篇文章似乎解决了您关于 fsync 的问题: http://blogs.gnome.org/alexl/2009/03/16/ext4-vs-fsync-my-take/

    【讨论】:

    • 之前读过那篇文章,它是把我带到这里的众多文章之一。它特别指出:“如果在写入后不久发生系统崩溃,我们更有可能获得新文件而不是旧文件(为了获得最大机会,您还需要同步文件所在的目录)。”我的问题是当你跨目录重命名时会发生什么。
    猜你喜欢
    • 2018-07-02
    • 2012-04-01
    • 1970-01-01
    • 2014-02-07
    • 2023-03-13
    • 1970-01-01
    • 2020-03-23
    • 1970-01-01
    • 2011-10-30
    相关资源
    最近更新 更多