Java Unix 中的移动操作是原子的吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18706419/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 10:20:53  来源:igfitidea点击:

Is a move operation in Unix atomic?

javaunixfile-locking

提问by Chaos

Suppose there are 2 processes P1 and P2, and they access a shared file Foo.txt.

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

Suppose P2 is reading from Foo.txt. I don't want P1 to write to Foo.txtwhile P2 is reading it.

假设 P2 正在读取Foo.txt。我不希望 P1Foo.txt在 P2 读取时写入。

So I thought I could make P1 write to Foo.tmpand as a last step, rename Foo.tmpto Foo.txt. My programming language is Java

所以我想我可以让 P1 写入Foo.tmp并作为最后一步,重命名Foo.tmpFoo.txt. 我的编程语言是 Java

So my question is, would this ensure that P2 reads the correct data from Foo.txt? Would the rename operation be committed once P2 completes reading the file?

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

EDIT

编辑

I tried to recreate this scenario as follows:

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

My P1 code is something like this:

我的 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);

and my P2 code is :

我的 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();
}

My Sample shared File:

我的示例共享文件:

Test Input
Test Input
Test Input   

I'm starting P1 and P2 almost simulataneously (P2 starting first).

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

So according to my understanding, even though P1 has written a new Foo.txt, since P2 is already reading it, it should read the old Foo.txt content until it re-opens a BufferedReader to Foo.txt.

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

But what actually happens is P2 reads Test Inputthrice, as is expected from the input, but after that it reads the new content which was written by P1.

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

Output from P2:

P2 的输出:

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

So it doesn't work as it should. Am I testing this scenario wrong? I feel like there's something I'm missing out.

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

回答by Claudiu

  1. move(rename) is atomic if done on the same device. (device = same disk/partition)
  2. If Foo.txtexits move Foo.tmpto Foo.txtmost likely will fail. (But if you first delete Foo.txtand then move, it should work). What happens is that a file is not physically deleted until all file handlers are closed (there is no process that uses that file). Also, after remaining Foo.tmpto Foo.txtyou will have 2 Foo.txt files. One that is deleted but still opened in memory (basically that file does not have a reference on disk anymore) and one that actually resides on disk.
  3. But, after move, in second process you need to reopen the file.
  1. 如果在同一设备上完成,移动(重命名)是原子的。(设备 = 相同的磁盘/分区)
  2. 如果Foo.txt退出移动Foo.tmpFoo.txt很可能会失败。(但如果你先删除Foo.txt然后移动,它应该可以工作)。发生的情况是在关闭所有文件处理程序之前不会物理删除文件(没有使用该文件的进程)。此外,在剩下Foo.tmpFoo.txt你之后,你将有 2 个 Foo.txt 文件。一个被删除但仍然在内存中打开(基本上该文件在磁盘上不再有引用)和一个实际驻留在磁盘上。
  3. 但是,移动后,在第二个过程中,您需要重新打开文件。

Let me know if we are on the same page with #1.

让我知道我们是否与#1 处于同一页面。

回答by Kent

回答by Chris Dodd

A UNIX renameoperation is atomic (see rename(2)). The UNIX mvcommand uses rename if the source and target path are on the same physical device. If the target path is on a different device, the rename will fail, and mvwill copy the file (which is not atomic).

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

If the target file path exists, the renamewill atomically remove it from the file system and replace it with the new file. The file won't actually be deleted until its reference count drops to zero, so if another process is currently reading the file, it will keep reading the old file. Once all processes have closed the old file, its reference count will drop to zero and the file storage space will be reclaimed.

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