是否需要同时使用O_TRUNC和O_APPEND



我正在翻阅《The Linux Programming Interface》一书。在第4章第73页,上面写着:

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()将开始的位置。

我感到困惑的是,如果文件被截断,内核在文件末尾进行后续写入,为什么需要append标志来明确告知在文件末尾追加?

在没有append标志的情况下(如果文件被截断),内核会在文件的末尾为后续的write()函数调用进行写入。

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

这是真的,但不够完整,可能会产生误导。我怀疑你实际上在这方面感到困惑。

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

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

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

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

在没有append标志的情况下(如果文件被截断),内核会在文件的末尾进行写入,以进行后续的write()函数调用。

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

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

O_APPENDO_TRUNC很少有意义。我认为fopen模式的任何组合都不会产生这种组合(在POSIX系统上,这是相关的)。

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

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

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

O_APPEND不需要通过被理解为唯一写入程序的单个进程来附加到文件。可以只搜索到最后,然后开始写入新数据。有时O_APPEND在独占情况下使用,只是因为它是一种编程快捷方式。我们不必麻烦打额外的电话来定位到文件的末尾。比较:

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进行原子(如果可用)附加。

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

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

相关内容

  • 没有找到相关文章

最新更新