移动正在附加的文件是否安全?

移动正在附加的文件是否安全?

我有一个 node.js 进程,用于fs.appendFile将行添加到file.log.仅附加每行约 40 个字符的完整行,例如,调用类似于fs.appendFile("start-end"),而不是 2 个调用,例如fs.appendFile("start-")fs.appendFile("end")。如果我将此文件移动到,file2.log我可以确保没有行丢失或部分复制吗?

答案1

只要您不跨文件系统边界移动文件,操作就应该是安全的。这是由于机制,即“移动”实际上是如何完成的。

如果您的mv文件位于同一文件系统上,则该文件实际上并未被触及,而只是更改了文件系统条目。

$ mv foo bar

实际上做了类似的事情

$ ln foo bar
$ rm foo

这将创建一个难的文件(实际上是文件系统条目指向的索引节点)的链接(第二个目录条目)foo命名bar并删除该foo条目。由于现在删除 时foo,有第二个文件系统条目指向 的fooinode,因此删除旧条目foo实际上不会删除属于该 inode 的任何块。

无论如何,您的程序都会愉快地附加到文件,因为它的打开文件句柄指向文件的索引节点,而不是文件系统条目。

笔记:如果您的程序在写入之间关闭并重新打开文件,您最终会得到一个新的使用旧文件系统条目创建的文件!

跨文件系统移动:

如果您跨文件系统边界移动文件,事情就会变得很糟糕。在这种情况下,您无法保证文件保持一致,因为mv实际上会

  • 在目标文件系统上创建一个新文件
  • 将旧文件的内容复制到新文件
  • 删除旧文件

或者

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

分别。

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

根据写入应用程序期间复制是否到达文件末尾,新文件中可能只有半行。

此外,如果您的应用程序没有关闭并重新打开旧文件,它会继续写入旧文件,即使它似乎已被删除:内核知道哪些文件已打开,尽管它会删除文件系统条目,但它会删除它。在您的应用程序关闭其打开的文件句柄之前,不会删除旧文件的索引节点和关联的块。

答案2

既然你说你正在使用node.js,我假设你会使用fs.rename()(或fs.renameSync())重命名文件。这个node.js方法被记录为使用重命名(2)系统调用,它不会以任何方式触及文件本身,而只是更改文件系统中列出的名称:

改名() 重命名文件,并根据需要在目录之间移动它。该文件的任何其他硬链接(使用创建的关联(2))不受影响。打开文件描述符旧路径也不受影响。”

特别要注意上面引用的最后一句话,它说任何打开的文件描述符(例如您的程序将用来写入文件)即使在重命名后也将继续指向它。因此,即使在写入文件的同时重命名文件,也不会发生数据丢失或损坏。


正如安德烈亚斯·韦斯 (Andreas Weise) 在他的回答, rename(2) 系统调用(因此fs.rename()在 Node.js 中)将无法跨文件系统边界工作。因此,尝试以这种方式将文件移动到不同的文件系统将会失败。

Unixmv命令尝试通过检测错误来隐藏此限制,而是通过将文件内容复制到新文件并删除原始文件来移动文件。不幸的是,像这样移动文件如果在写入文件时移动文件,则存在数据丢失的风险。因此,如果您想安全地重命名可能同时写入的文件,您应该不是使用mv(或者至少,您应该绝对确保新路径和旧路径位于同一文件系统上)。

相关内容