我依稀记得在某处读过,在某些 Unices 中,曾经有一种打开现有文件进行写入的方法,带有一个要求内核使用旧版本的标志(供其他进程访问它进行读取),直到“新版本” " 版本已完全写入(fd 已关闭),从此时起该文件显示为新版本。
换句话说,其他进程要么看到旧版本,要么看到新版本,而绝不会看到不完整编写的版本。
有了解的人可以给我指点参考吗?
答案1
您所描述的内容听起来与覆盖文件的基本重命名完全相同。
当您重命名/移动一个文件到另一个文件之上时,旧文件将被取消链接。这意味着该文件仍然存在,但它不再位于文件系统树中。因此,只要旧应用程序保持文件打开状态,就可以继续访问该文件。一旦所有应用程序都关闭了旧文件,那么它实际上在磁盘上未分配。
系统rename
调用是一个原子操作。因此,要执行此操作,您需要使用不同的名称创建一个新文件,然后调用rename
将临时文件重命名为您想要替换的文件。由于操作是原子的,因此绝对不存在文件丢失的情况。它立即从旧文件转到新文件。
但请注意,临时文件和被替换的文件必须驻留在同一安装点上。
答案2
作为帕特里克写道,通常的方法是将新版本写入单独的文件,完成后将新版本重命名为旧文件名,自动覆盖它。第二个操作称为重命名覆盖。
现在,一些参考:
ISO C 要求
rename
是原子的。来自 Open Group 基本规格:如果链接命名为新的争论存在,应将其删除并老的重命名为 新的。在本例中,名为的链接新的在整个重命名操作期间应对其他进程保持可见,并引用由新的或者老的在手术开始之前。
Btrfs 显然故意违反了标准不保证原子重命名,出于性能原因。然而,重命名覆盖仍然是原子的,这就是您为此目的所需的全部内容。
答案3
这让我想起刷新时分配。当文件系统使用此功能时,它不是直接将数据写入磁盘,而是从磁盘的可用空间计数器中减去要写入的数据的大小,并将数据保存在内存中,直到执行同步系统调用或内核决定刷新脏缓冲区。
在这种情况下,如果文件正在被一个进程修改,并被另一个进程打开,则后一个进程将“看到”未修改的(或“旧”(如果您愿意)) 文件的版本。
当然,以上是理论上的,取决于各种因素,我想说有点不可预测 - 因为你不知道内核何时会刷新脏页。例如在 Linux 中(您还可以阅读《理解 Linux 内核》的 15.3 节),脏页在以下条件下被写入磁盘:
页面缓存太满,需要更多页面,或者脏页面数量变得太大。
自从页面保持脏状态以来已经过去了太多时间。
进程请求刷新块设备或特定文件的所有挂起更改;它通过调用sync()、fsync()或fdatasync()系统调用来实现这一点。
已知此功能已在 HFS+、XFS、Reiser4、ZFS、Btrfs 和 ext4 文件系统中实现。