重命名()原子?

Lip*_*eka 40 linux file-rename vfs atomicity

我无法通过实验检查这一点,也无法从手册页中收集它.

假设我有两个进程,一个从directory1移动(重命名)file1到directory2.假设另一个并发运行的进程将directory1和directory2的内容复制到另一个位置.是否有可能以这样的方式发生复制:directory1和directory2都将显示file1 - 即在移动之前复制directory1,在第一个进程移动之后复制directory2.

基本上是rename()是一个原子系统调用?

谢谢

Adr*_*ala 24

这是一个非常晚的答案,但是......是rename()原子的,但不是你的问题.在Linux下, rename(2)说:

但是,当覆盖时,可能会有一个窗口,其中oldpath和newpath都引用要重命名的文件.

但是rename()在一个非常重要的意义上它仍然是原子的:如果你用它来覆盖一个文件,那么你最终将使用旧版本或新版本,而不是其他任何东西.

[ 更新:但正如@ jonas-wielicki在评论中指出的那样,您需要确保您重命名的文件实际上具有最新内容,使用fsync()和朋友.]

如果newpath已经存在,它将被原子替换(受一些条件限制;请参阅下面的错误),这样就没有任何一点可以尝试访问newpath的另一个进程会发现它丢失.

如果您看到ERRORS,您会发现重命名可能会失败,但它永远不会破坏原子性.

这完全来自Linux手册页.我不知道的是,如果你rename()在服务器运行不同操作系统的网络文件系统上进行操作.客户是否希望保证原子性呢?我对此表示怀疑.

  • 我想在覆盖用例中添加它,在重命名之前调用flush非常重要,以确保数据实际写入文件.否则,如果发生崩溃,最终可能只有一个空文件(rename()成功,数据尚未写入磁盘,然后崩溃 - >空文件). (3认同)
  • 重命名文件夹是否也一样? (2认同)

Jos*_*hua 23

是的,不是.

rename()是原子的,假设操作系统没有崩溃.它不能被任何其他文件系统op拆分.

如果系统崩溃,您可能会看到ln()操作.

另请注意,在网络文件系统上运行时,如果操作成功,您可能会获得ENOENT.本地文件系统无法做到这一点.

  • 我的源代码"如果系统崩溃,你可能会看到ln()操作." 是内核源代码本身. (6认同)

boa*_*der 6

我不确定你问题的"基本"部分是否有效.除非你们之间有某种同步,否则原子重命名的方式并不重要.如果目录副本在重命名之前到达那里,那么你将在两个地方都有file1.

我不确定你是否意味着线程或进程,但如果两者都有锁定机制,则线程锁是最简单的,因为它们不必跨越进程边界.

  • @Joshua:是的,但是Mark0978是正确的:OP描述的过程是racy*,即使一个文件系统上的``rename()`是原子的(因为读取两个不同目录的内容不是原子的,所以重命名可以在读取directory1之后和读取directory2之前发生了. (3认同)
  • @Joshua:您需要重新阅读原始问题.它有一个进程`rename()`,与另一个进程竞争,即*"将directory1和directory2的内容复制到另一个位置"*.第二个进程至少需要*"读取目录1的内容"*和*"读取目录2的内容"*步骤,甚至*之前*它自己进行任何复制/重命名,这是*这些*步骤可以在第一个进程中使用`rename()`进行竞争. (2认同)