我隐约记得在某些地方阅读某处曾经有一种打开现有文件进行写入的方式,它带有一个标志,要求内核使用旧版本(供其他进程访问以进行读取),直到“新版本已完全写入(fd已关闭),此后文件将显示为新版本。
换句话说,其他进程要么看到的是旧版本,要么看到的是新版本,而从来没有看到不完整的版本。
有知识的人可以为我提供参考吗?
我隐约记得在某些地方阅读某处曾经有一种打开现有文件进行写入的方式,它带有一个标志,要求内核使用旧版本(供其他进程访问以进行读取),直到“新版本已完全写入(fd已关闭),此后文件将显示为新版本。
换句话说,其他进程要么看到的是旧版本,要么看到的是新版本,而从来没有看到不完整的版本。
有知识的人可以为我提供参考吗?
Answers:
您所描述的内容听起来完全像是用于覆盖文件的基本重命名。
重命名/移动一个文件到另一个文件时,旧文件将取消链接。意味着该文件仍然存在,但不再存在于文件系统树中。因此,只要保持打开状态,旧应用程序将继续能够访问该文件。一旦所有应用程序都关闭了旧文件,则实际上将其未分配到磁盘上。
该rename
系统调用是一个原子操作。因此,您可以使用其他名称创建一个新文件,然后调用rename
将临时文件重命名为您要替换的文件。由于此操作是原子操作,因此绝对没有文件丢失的时间段。它立即从旧文件变为新文件。
请注意,尽管临时文件和要替换的文件必须位于同一安装点上。
rename
交换任务。即使您所说的“操作系统功能”已经存在,也必须编写程序来利用它。有什么不同?
open
syscall,或者必须手动执行描述的操作,则有所不同。
正如Patrick所写,执行此操作的通常方法是将新版本写入单独的文件,并在完成后将新版本重命名为旧文件名,并自动进行覆盖。第二个操作称为overwrite-by-rename。
ISO C要求rename
是原子的。来自开放组基本规范:
如果由new参数命名的链接存在,则应将其删除并将旧名称重命名为 new。在这种情况下,在整个重命名操作中,名为new的链接将对其他进程保持可见,并在操作开始之前引用new或old所引用的文件。
由于性能原因,Btrfs显然不保证原子重命名而故意违反了该标准。但是,按重命名覆盖仍然是atomic,这是您为此目的所需要的。
man 3p rename
告诉我这rename
确实是原子的,我想这对所有Linux文件系统都有意义。当我阅读您链接的第一篇文章时,我仍然认为Btrfs重命名操作是原子的。
这使我想起了“ 冲洗时分配”。当文件系统使用此功能时,它不是从磁盘的可用空间计数器中减去要写入的数据大小,而是将数据直接写入磁盘,而是将数据保留在内存中,直到执行同步系统调用或内核决定冲洗脏缓冲区。
在这种情况下,如果文件正在被一个进程修改,而又被另一个进程打开,则后一个进程将“看到”文件的未修改(或“旧”)版本。
当然,以上内容是理论上的,并且取决于各种因素,我会说出一些不可预测的信息,因为您不确切知道内核何时清除脏页。例如在Linux中(您也可以在了解Linux内核的15.3节中阅读)中,在以下情况下,脏页将被写入磁盘:
页面缓存变得太满,需要更多页面,或者脏页面数变得太大。
自页面变脏以来,已经花费了太多时间。
进程要求清除块设备或特定文件的所有未完成的更改;它通过调用sync(),fsync()或fdatasync()系统调用来实现。
已知此功能可以在HFS +,XFS,Reiser4,ZFS,Btrfs和ext4文件系统中实现。