【问题标题】:Is it required to use O_TRUNC and O_APPEND together?是否需要同时使用 O_TRUNC 和 O_APPEND?
【发布时间】:2023-04-04 14:54:01
【问题描述】:

我正在阅读The Linux Programming Interface 的书。在页号。 73第4章 这是写的

fd = open("w.log", O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, S_IRUSR | S_IWUSR);

我读到O_TRUC 标志用于将文件长度截断为零,这会破坏文件中的任何现有数据。

O_APPEND 标志用于将数据追加到文件末尾。

内核记录file offset,有时也称为read-write offset or pointer。这是文件中下一个read()write() 将开始的位置。

我很困惑,如果文件被截断并且内核在文件末尾进行后续写入,为什么需要附加标志来明确告诉在文件末尾追加?

如果没有附加标志(如果文件被截断),内核会在文件末尾写入后续write() 函数调用。

请帮忙,谢谢。

【问题讨论】:

    标签: linux posix write fcntl


    【解决方案1】:

    O_APPEND 标志用于将数据追加到文件末尾。

    确实如此,但不够完整,可能会产生误导。我怀疑您实际上在这方面感到困惑。

    内核记录一个文件偏移量,有时也称为读写偏移量或指针。这是文件中下一个read()write() 将开始的位置。

    这也不完整。至少每个可查找文件都有一个文件偏移量。那是下一个read() 将开始的位置。这是下一个write() 将开始的地方如果文件没有以追加模式打开,但在追加模式下每次写入都发生在文件末尾,就好像它在每个之前都用lseek(fd, 0, SEEK_END) 重新定位。在这种情况下,当前文件偏移量可能不是下一个write() 开始的位置。

    我很困惑,如果文件被截断并且内核在文件末尾进行后续写入,为什么需要附加标志来明确告诉在文件末尾追加?

    不需要在文件末尾发生截断后的第一次写入(由任何进程),因为在文件被截断后立即没有任何其他位置。

    如果没有附加标志(如果文件被截断),内核会在文件末尾写入以供后续的 write() 函数调用。

    后续写入也不需要它,只要文件没有被重新定位或外部修改。否则,下一次写入的位置取决于文件是否以追加模式打开。

    在实践中,不一定每个标志组合都有用,但 O_TRUNCO_APPEND 的组合与没有另一个标志的组合具有明显不同的效果,并且组合在某些情况下很有用情况。

    【讨论】:

      【解决方案2】:

      O_APPEND 很少与O_TRUNC 一起使用。我认为 C fopen 模式的任何组合都不会产生这种组合(在 POSIX 系统上,这是相关的)。

      O_APPEND 确保每次写入都在文件末尾自动完成,无论写入位置如何。特别是,这意味着如果多个进程正在写入文件,它们不会踩踏彼此的写入。

      请注意,POSIX 不需要O_APPEND 的原子行为。它要求在写入之前自动搜索到文件的(当前)末尾,但它并不要求在写入时该位置仍然是文件的末尾。即使在具有原子O_APPEND 的实现上,它也可能不适用于所有文件系统。 open 上的 Linux 手册页警告说,O_APPEND 在 NFS 上不能以原子方式工作。

      现在,如果每个进程在打开文件时都使用O_TRUNC,它将破坏所有其他进程写入的所有内容。这与进程不应该破坏彼此的写入的想法相冲突,为此指定了O_APPEND

      O_APPEND 不需要通过单个进程附加到文件,该进程被理解为唯一的写入者。可以只搜索到最后,然后开始写入新数据。有时O_APPEND 无论如何都用于独占情况,只是因为它是一种编程快捷方式。我们不必费心额外调用 position 到文件末尾。比较:

      FILE *f = fopen("file.txt", "a");
      // check f and start writing
      

      对比:

      FILE *f = fopen("file.txt", "r+");
      // check f
      fseek(f, 0, SEEK_END); // go to the end, also check this for errors
      // start writing
      

      我们可以考虑这样的想法,我们有一组进程使用O_APPEND 到一个文件,这样第一个也执行O_TRUNC 以首先截断它。但是这样编程似乎很尴尬;进程要判断它是否是第一个打开文件的进程并不容易。

      如果在启动时需要这种情况,其中启动前的旧文件由于某种原因无关紧要,只需在启动时执行一个操作(脚本或其他)在这些多个之前删除旧文件进程启动。然后每个人都使用O_CREAT 在必要时创建文件(如果它是第一个进程)但没有O_TRUNC(如果它们不是第一个进程),并使用O_APPEND 来执行原子(如果可用) ) 附加的东西。

      【讨论】:

        【解决方案3】:

        两者是完全独立的。该文件只是用O_APPEND 打开,因为它是一个日志文件。

        作者希望并发消息连接而不是相互覆盖(例如,如果程序分叉),并且如果管理员或日志轮换工具截断文件,那么新消息应该在第 1 行而不是第 # 行开始写入1000000 写入最后一个日志条目的位置。如果没有 O_APPEND,这将不会发生。

        【讨论】:

          猜你喜欢
          • 2020-05-10
          • 2017-02-28
          • 1970-01-01
          • 1970-01-01
          • 2011-06-11
          • 1970-01-01
          • 2010-11-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多