重写现有文件,以便它以原子方式被新版本替换,只有一次完全写入

eud*_*xos 18 kernel open-files

我依稀记得曾经在某个地方读过,在某些 Unices 中,一种打开现有文件进行写入的方法,带有一个标志,要求内核使用旧版本(供其他进程访问它以进行读取),直到“新的" 版本已完全写入(fd 关闭),从那时起该文件显示为新版本。

换句话说,其他进程要么看到旧版本,要么看到新版本,从来没有一个不完整的版本。

有知识的人可以给我指点参考吗?

phe*_*mer 14

您所描述的听起来与覆盖文件的基本重命名完全一样。

当您将一个文件重命名/移动到另一个文件之上时,旧文件将被取消链接。这意味着该文件仍然存在,但它不再在文件系统树中。因此,只要旧应用程序保持打开状态,它们就可以继续访问该文件。一旦所有应用程序都关闭了旧文件,那么它实际上未分配到磁盘上。

rename系统调用是一个原子操作。为此,您需要使用不同的名称创建一个新文件,然后调用rename将临时文件重命名为要替换的文件。由于操作是原子的,因此绝对没有丢失文件的时间段。它立即从旧文件转到新文件。
请注意,临时文件和被替换的文件必须驻留在同一个挂载点上。


Mec*_*ail 6

正如帕特里克所写,通常的做法是将新版本写入一个单独的文件,完成后将新版本重命名为旧文件名,以原子方式覆盖它。第二个操作称为overwrite-by-rename

现在,一些参考:


dka*_*dis 0

这让我想起Allocate On Flush。当文件系统使用此功能时,它不是直接将数据写入磁盘,而是从磁盘的可用空间计数器中​​减去要写入的数据的大小,并将数据保存在内存中,直到执行同步系统调用或内核决定刷新脏缓冲区。

在这种情况下,如果文件正在被一个进程修改,并被另一个进程打开,则后一个进程将“看到”文件的未修改(或“旧” )版本。

当然,以上是理论上的,取决于各种因素,我想说有点不可预测 - 因为你不知道内核何时会刷新脏页。例如,在 Linux 中(您也可以阅读《理解 Linux 内核》的 15.3 节),脏页在以下条件下被写入磁盘:

  • 页面缓存太满,需要更多页面,或者脏页面数量变得太大。

  • 自从页面保持脏状态以来已经过去了太多时间。

  • 进程请求刷新块设备或特定文件的所有挂起更改;它通过调用sync()、fsync()或fdatasync()系统调用来实现这一点。

已知此功能已在 HFS+、XFS、Reiser4、ZFS、Btrfs 和 ext4 文件系统中实现。

  • 您所描述的是一种文件系统技术,该技术应该在 POSIX(文件)系统上的用户空间中不可见(因此不会执行您指示的操作)(请参阅 [write](http://pubs.opengroup.org/onlinepubs/007904975/functions /write.html): “如果可以证明(通过任何方式)文件数据的 read() 发生在数据的 write() 之后,它必须反映 write(),**即使进行了调用通过不同的过程**。”)。其他进程不会看到旧数据(在 POSIX 上)。 (4认同)