我无法通过实验检查这一点,也无法从手册页中收集它。
假设我有两个进程,一个将文件 1 从目录 1 移动(重命名)到目录 2。假设同时运行的另一个进程将 directory1 和 directory2 的内容复制到另一个位置。复制是否有可能以这样一种方式发生,即 directory1 和 directory2 都将显示 file1 - 即 directory1 在移动之前被复制,而 directory2 在第一个进程移动之后被复制。
基本上 rename() 是一个原子系统调用吗?
谢谢
我无法通过实验检查这一点,也无法从手册页中收集它。
假设我有两个进程,一个将文件 1 从目录 1 移动(重命名)到目录 2。假设同时运行的另一个进程将 directory1 和 directory2 的内容复制到另一个位置。复制是否有可能以这样一种方式发生,即 directory1 和 directory2 都将显示 file1 - 即 directory1 在移动之前被复制,而 directory2 在第一个进程移动之后被复制。
基本上 rename() 是一个原子系统调用吗?
谢谢
是和不是。
假设操作系统没有崩溃,rename() 是原子的。它不能被任何其他文件系统操作拆分。
如果系统崩溃,您可能会看到 ln() 操作。
另请注意,在网络文件系统上操作时,您可能会在操作成功时获得 ENOENT。本地文件系统不能对你这样做。
这是一个很晚的答案,但是......是的,rename()
但在你的问题的意义上不是原子的。在 Linux 下, rename(2)
说:
但是,在覆盖时,可能会出现一个窗口,其中 oldpath 和 newpath 都引用被重命名的文件。
但是rename()
在一个非常重要的意义上仍然是原子的:如果你用它来覆盖一个文件,那么你最终会得到旧版本或新版本,没有别的。
[更新:但正如@jonas-wielicki 在评论中指出的那样,您需要确保您正在重命名的文件实际上具有最新的内容,使用fsync()
和朋友。]
如果 newpath 已经存在,它将被原子地替换(受一些条件的影响;请参阅下面的错误),以便尝试访问 newpath 的另一个进程不会发现它丢失。
如果你看到 ERRORS,你会发现重命名可能会失败,但它永远不会破坏原子性。
这全部来自 Linux 手册页。我不知道您是否rename()
在服务器运行不同操作系统的网络文件系统上执行操作。那么客户是否有希望保证原子性呢?我对此表示怀疑。
我不确定您问题的“基本上”部分是否有效。除非您在两者之间进行某种同步,否则原子重命名的方式无关紧要。如果目录副本在重命名之前到达那里,那么您将在两个地方都有 file1。
我不确定您是指线程还是进程,但如果两者都有锁定机制,那么线程锁是迄今为止最简单的,因为它们不必跨越进程边界。
gnu libc手册说
rename 的一个有用特性是 newname 的含义从任何以前存在的以该名称命名的文件“原子地”更改为它的新含义(即,称为 oldname 的文件)。在旧含义和新含义“之间”没有新名称不存在的瞬间。如果在操作过程中出现系统崩溃,则两个名称都可能仍然存在;但是如果 newname 存在的话,它将永远是完整的。