【问题标题】:Is a move operation in Unix atomic?Unix中的移动操作是原子的吗?
【发布时间】:2013-09-09 17:22:36
【问题描述】:

假设有 2 个进程 P1 和 P2,它们访问一个共享文件 Foo.txt

假设 P2 正在从Foo.txt 读取数据。我不希望 P1 在 P2 读取它时写信给Foo.txt

所以我想我可以让 P1 写信给Foo.tmp,最后一步,将Foo.tmp 重命名为Foo.txt。我的编程语言是Java

所以我的问题是,这能确保 P2 从 Foo.txt 读取正确的数据吗?一旦P2完成读取文件,是否会提交重命名操作?

编辑

我尝试按如下方式重新创建此场景:

我的 P1 代码是这样的:

File tempFile = new File(path1);
File realFile = new File(path2);
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
for(int i=0;i<10000;i++)
    writer.write("Hello World\n");
writer.flush();
writer.close();
tempFile.renameTo(realFile);

我的 P2 代码是:

BufferedReader br = new BufferedReader(new FileReader(file)); 
String line = null;
while(true) {
  while((line=br.readLine())!=null){
      System.out.println(line);
      Thread.sleep(1000);
  }
  br.close();
}

我的示例共享文件:

Test Input
Test Input
Test Input   

我几乎同时启动 P1 和 P2(P2 先启动)。

所以根据我的理解,即使P1已经写了一个新的Foo.txt,由于P2已经在读取它,它应该读取旧的Foo.txt内容,直到它重新打开一个BufferedReader到Foo.txt。

但实际发生的是 P2 读取了三次Test Input,正如输入所预期的那样,但之后它读取了 P1 写入的新内容。

P2 的输出:

Test Input
Test Input
Test Input 
Hello World
Hello World
Hello World
 .
 .
 .

所以它不能正常工作。我测试这个场景错了吗?我觉得我错过了什么。

【问题讨论】:

  • 你有没有考虑过使用数据库来做这样的事情?你会得到一个历史,你的第二个过程可以只是select text from foo where insertstamp = max(insertstamp)
  • @corsiKa,无法使用数据库。有问题的共享文件与许多必须更改的模块交互。
  • 这确实是 Unix 中的常用习语(atomic-rename)。尽管在 C 中,rename() 之前通常有一个fsync(),即使在操作系统/硬件崩溃时也能提供排序保证。
  • 您的 P2 代码看起来每次循环都在关闭文件,因此下一次迭代将不得不重新打开文件(如果它已被更改,它将获取新文件)。如果没有显式重新打开,我希望它会失败,但我不知道 java,所以我猜它是在隐式重新打开文件?
  • 我认为它不会在每次迭代时重新打开文件。我这么说的原因是因为我在 P2 的 1 次迭代后运行 P1(当它打印一次 Test Input 时。所以如果它在每次迭代时重新打开文件,在第 2 次迭代时,它应该打印新数据。但它三次打印Test Input,这意味着它没有关闭文件

标签: java unix file-locking


【解决方案1】:

UNIX rename 操作是原子操作(请参阅 rename(2))。如果源路径和目标路径位于同一物理设备上,则 UNIX mv 命令使用重命名。如果目标路径在不同的设备上,重命名将失败,mv 将复制文件(不是原子的)。

如果目标文件路径存在,rename 将自动将其从文件系统中删除并替换为新文件。该文件实际上不会被删除,直到其引用计数降至零,因此如果另一个进程当前正在读取该文件,它将继续读取旧文件。一旦所有进程都关闭了旧文件,其引用计数将降至零,文件存储空间将被回收。

【讨论】:

  • RAID 设置中的磁盘是否也被视为同一个物理设备?
【解决方案2】:

为什么不使用FileChannel.lock

这是一个例子:

http://examples.javacodegeeks.com/core-java/nio/filelock/create-shared-file-lock-on-file/

【讨论】:

  • 是的,我在发布我的问题后最终使用了它,但我仍然很好奇它是如何工作的
【解决方案3】:
  1. move(rename) 如果在同一设备上完成,则为原子操作。 (设备 = 相同的磁盘/分区)
  2. 如果Foo.txt 退出,将Foo.tmp 移动到Foo.txt 很可能会失败。 (但如果你先删除Foo.txt 然后移动,它应该可以工作)。发生的情况是,直到所有文件处理程序都关闭(没有使用该文件的进程),文件才被物理删除。此外,在剩余 Foo.tmpFoo.txt 之后,您将拥有 2 个 Foo.txt 文件。一个已删除但仍在内存中打开(基本上该文件在磁盘上不再有引用),一个实际驻留在磁盘上。
  3. 但是,在移动之后,在第二个过程中您需要重新打开文件。

如果我们与 #1 在同一页面上,请告诉我。

【讨论】:

  • 您所说的“设备”是什么意思,请您详细说明一下吗?
  • 是的,我们和#1在同一个页面上
  • @Chaos 我更改了对 #2 的解释,以明确实际发生的情况。
  • 在现有文件上移动也可以。否则将无法例如。在多用户系统中安全地替换/tmp/ 下的文件。
猜你喜欢
  • 2010-11-10
  • 2015-01-10
  • 2016-11-05
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
  • 2017-08-24
  • 1970-01-01
  • 2018-01-06
相关资源
最近更新 更多