一个已经打开的 FILE 句柄是否可以在不重新打开它的情况下反映对基础文件的更改?
Can an already opened FILE handle reflect changes to the underlying file without re-opening it?
假设一个纯文本文件,foo.txt
,和两个进程:
- 进程 A,一个 shell 脚本,定期覆盖文件
$ echo "example" > foo.txt
- 进程 B,一个 C 程序,定期从文件中读取
fopen("foo.txt", "r"); getline(buf, len, fp); fclose(fp);
在 C 程序中,在初始 fopen()
之后保持 FILE* fp
打开,执行 rewind()
并再次读取似乎没有反映文件发生的变化同时。是通过 fclose()
和 fopen()
循环查看更新内容的唯一方法,还是有办法重新使用已经打开的 FILE
句柄,但读取最近写入的内容数据?
对于上下文,我只是想找到最有效的方法。
在 Unix/Linux 上,当您使用已存在的名称创建文件时,不会以任何方式删除或更改旧文件。创建了一个新文件,目录 更新为指向新文件而不是旧文件。
只要某些目录入口指向旧文件(Unix 文件系统允许多个目录指向同一个文件)或某些程序具有该文件的打开文件句柄,旧文件将继续存在,这与您的问题更相关。
只要您不关闭 fp
,它就会继续引用原始文件,即使文件系统不再引用该文件。当您关闭 fp
时,该文件将自动进行垃圾收集,而下次您打开 foo.txt
时,您将获得一个文件描述符,该文件描述符代表当时碰巧具有该名称的任何文件。
简而言之,对于您指定的 shell 脚本,您的 C 程序必须关闭并重新打开文件才能看到新内容。
理论上,shell 脚本可以覆盖同一个文件而不删除它,但是 (a) 这很难做到正确; (b) 它容易出现竞争条件; (c) 关闭和重新打开文件不是 time-consuming。但如果你这样做,你会看到变化。 [注1]
特别是,附加到现有文件是很常见的(也很容易),如果你有一个 shell 脚本可以做到这一点,你可以保留打开文件描述符并查看更改。但是,在那种情况下,您通常会在追加新数据之前读取到文件末尾,并且标准 C 库将 feof()
指示符视为粘性;一旦设置好,您将继续从新读取中获得 EOF 指示。如果您怀疑某些进程将向文件写入更多数据,您应该在重试读取之前使用 fseek(fp, 0, SEEK_CUR);
重置 EOF 指示。
备注
- 正如 @amadan 在评论中指出的那样,
echo text > foo.txt
也存在竞争条件,尽管 window 更短一些。但是你绝对可以通过使用惯用语 echo text > temporary_file; mv -f temporary_file foo.txt
来避免竞争条件,因为重命名操作是原子的。当然,这肯定需要您关闭并重新打开文件。但这是一个好主意,尤其是当正在写入的内容很长或很关键,或者如果经常创建新文件时。
假设一个纯文本文件,foo.txt
,和两个进程:
- 进程 A,一个 shell 脚本,定期覆盖文件
$ echo "example" > foo.txt
- 进程 B,一个 C 程序,定期从文件中读取
fopen("foo.txt", "r"); getline(buf, len, fp); fclose(fp);
在 C 程序中,在初始 fopen()
之后保持 FILE* fp
打开,执行 rewind()
并再次读取似乎没有反映文件发生的变化同时。是通过 fclose()
和 fopen()
循环查看更新内容的唯一方法,还是有办法重新使用已经打开的 FILE
句柄,但读取最近写入的内容数据?
对于上下文,我只是想找到最有效的方法。
在 Unix/Linux 上,当您使用已存在的名称创建文件时,不会以任何方式删除或更改旧文件。创建了一个新文件,目录 更新为指向新文件而不是旧文件。
只要某些目录入口指向旧文件(Unix 文件系统允许多个目录指向同一个文件)或某些程序具有该文件的打开文件句柄,旧文件将继续存在,这与您的问题更相关。
只要您不关闭 fp
,它就会继续引用原始文件,即使文件系统不再引用该文件。当您关闭 fp
时,该文件将自动进行垃圾收集,而下次您打开 foo.txt
时,您将获得一个文件描述符,该文件描述符代表当时碰巧具有该名称的任何文件。
简而言之,对于您指定的 shell 脚本,您的 C 程序必须关闭并重新打开文件才能看到新内容。
理论上,shell 脚本可以覆盖同一个文件而不删除它,但是 (a) 这很难做到正确; (b) 它容易出现竞争条件; (c) 关闭和重新打开文件不是 time-consuming。但如果你这样做,你会看到变化。 [注1]
特别是,附加到现有文件是很常见的(也很容易),如果你有一个 shell 脚本可以做到这一点,你可以保留打开文件描述符并查看更改。但是,在那种情况下,您通常会在追加新数据之前读取到文件末尾,并且标准 C 库将 feof()
指示符视为粘性;一旦设置好,您将继续从新读取中获得 EOF 指示。如果您怀疑某些进程将向文件写入更多数据,您应该在重试读取之前使用 fseek(fp, 0, SEEK_CUR);
重置 EOF 指示。
备注
- 正如 @amadan 在评论中指出的那样,
echo text > foo.txt
也存在竞争条件,尽管 window 更短一些。但是你绝对可以通过使用惯用语echo text > temporary_file; mv -f temporary_file foo.txt
来避免竞争条件,因为重命名操作是原子的。当然,这肯定需要您关闭并重新打开文件。但这是一个好主意,尤其是当正在写入的内容很长或很关键,或者如果经常创建新文件时。