【问题标题】:C/C++: is there a way to create a nonzero-length file atomically?C/C++:有没有办法以原子方式创建非零长度文件?
【发布时间】:2020-01-09 14:23:58
【问题描述】:

我有一个线程监视目录中的文件添加(如果存在则使用inotify,否则轮询),并在监视目录中创建的新文件时通知侦听器。侦听器具有基于创建文件大小的条件逻辑,它使用int stat(const char *pathname, struct stat *statbuf) 确定。

在一个单独的线程中,我使用 std::ofstream 创建了一个非零长度文件;文件创建的简化示例是:

std::ofstream ofs( "/path/to/file", std::ofstream::out );
ofs << "abc";
ofs.close()

运行时行为是调用stat() 的侦听器有时将文件视为0 长度。

这是完全合理的,因为文件创建和内容添加是单独的操作。

问题:有没有办法使用C 函数或C++03stl 原子地创建非零长度文件?

注意:出于这个问题的目的,我对同步原语(如互斥锁或信号量)不感兴趣,以在文件打开、添加内容、关闭文件的整个过程中同步两个线程。

【问题讨论】:

  • 为什么不直接将现有文件复制到目录中?
  • 您可以在 不同的 目录中创建文件。关闭文件后,您可以将文件移动(重命名)到正确的目录。那么inotify 会检测到文件吗?
  • @NathanOliver - 我想知道:非零文件的副本是否可以作为非零文件的“原子”外观,或者副本本身是否经历(1 ) 0 长度文件创建,(2) 内容添加,(3) 文件关闭。
  • 据我了解,唯一不平凡的 POSIX 操作至少在某种程度上是原子的,至少在某种程度上是可移植的,它是不跨越文件系统/设备边界的rename。但是我的 POSIX-fu 很弱。
  • 我建议当您在单独的线程(例如 test.txt)中创建新文件时,将其命名为 text.txt.part 之类的名称,然后在您的第一个线程中让它忽略扩展名为 .part 的文件.然后,当您准备好时,将文件重命名为正确的名称。从长远来看,这实际上可能对您有所帮助,因为其他进程通常使用 .part 来表示文件不完整(例如,当您下载文件时,或者有人也在您上传文件时)。但是我不确定重命名它是否会注册为新文件。

标签: c++ c file filesystems


【解决方案1】:

有没有办法使用 C 函数或 C++03 的 stl 原子地创建非零长度文件?

最好的方法是在同一文件系统的其他位置创建文件,然后将std::rename 文件放入目标文件中。

该标准并没有真正给出明确的保证,除了后置代码(文件以新名称或旧名称存在)。与可观察的中间状态无关。在实践中,您受文件系统的支配。但是如果有一些标准的操作可以达到你想要的,那么就是这样。 POSIX 标准确实要求 rename 是原子的。

【讨论】:

  • 请注意,如果您正在监视整个目录,则需要忽略初始名称(或名称系列)或在同一设备的其他位置创建它。我不认为 stl 重命名规范保证支持重命名为 move,尽管底层操作系统调用可能会并且它不做任何检查,所以目录监视器上的过滤器是最强大的解决方案..
【解决方案2】:

基础 C 语言没有这样的概念,我认为 C++ 也没有。如果您在谈论这些类型的事情,那么您一定是在假设 POSIX 或其他一些操作系统级别的行为规范。

在POSIX下,进行这种操作的方法是创建一个临时名称的文件,然后在你写完之后rename它。如果它们都在同一设备上,您可以在不同的目录中执行此操作;如果它们在不同的设备上,是否可行是实现定义的。最便携的方法是在同一目录中执行此操作,这意味着您的inotify(Linux-specific,BTW)侦听器应忽略与其正在查找的命名模式不匹配的文件,或者忽略您选择的特定临时命名空间中的文件你的约定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-22
    • 2019-04-04
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2020-06-15
    • 1970-01-01
    相关资源
    最近更新 更多