【问题标题】:fopen for write never returns nullfopen for write 从不返回 null
【发布时间】:2014-07-17 07:03:32
【问题描述】:

我正在尝试构建一些简单的东西来测试文件锁定。我尝试打开两个文件进行写入,两个调用都完成。打开文件后显示:“hello world”。

#include <stdio.h>

int main() {
  File *fp1 = fopen("./test.txt", "w");
  fwrite("hello", 1, 5, fp1);

  File *fp2;
  if (fp2 = fopen("./test.txt", "w")) {
    fwrite("      world", 1, 11, fp2);
  }
}

从 fopen 的手册页中,我的印象是,如果打开文件进行写入,第二次调用将返回 null。我发现了一个类似的案例:Opening a file using fopen with same flag in C;按照 Adhip Gupta 的回答,我尝试检查

我也尝试使用 fcntl.h 中的 open()。当我期望第二次调用为 -1 时,打印了两个文件描述符。

#include <stdio.h>
#include <fcntl.h>

int main() {
  int id1 = open("./text.txt", O_WRONLY);
  int id2 = open("./text.txt", O_WRONLY);
  printf("%d %d\n", id1, id2);
}

为什么会这样?我进行测试时没有关闭文件流/描述符;这可能是一个原因吗?

【问题讨论】:

  • fopen 手册页在什么系统上这么说?可以发个报价单吗?
  • 只是因为手册页没有明确提到写入可以同时发生。我假设您不能打开两个文件进行写入,因为它会导致竞争条件。
  • 尚不清楚此问题与竞态条件有何关系。如果您通过 same 句柄同时写入,您可能会遇到竞争条件。但是你有责任把它信号量化掉。

标签: c file locking stdio


【解决方案1】:

这是因为当您使用"w" 作为fopen 的打开模式时,它会破坏文件的当前内容并从新文件开始。因此,当您再次调用fopen 时,它会成功,因为它不关心文件是否存在,或者其中是否有任何内容。如果路径正确,并且你有正确的权限打开文件进行写入,那么fopen调用就会成功。

如果你想打开一个只写的文件,如果它已经存在就失败,你必须先自己检查文件是否存在。

【讨论】:

  • 如果文件已经存在,您可以使用O_EXCLO_CREATopen 来失败。这可以防止检查和打开之间的竞争条件,如果您进行两个单独的函数调用,就会存在这种情况。但是,open 不是标准 C,而是 POSIX 的一部分,因此它可能适用于您的用例,也可能不适用于您的用例。至于“该状态存储在系统中”——我认为这并不复杂:如果你真的愿意,你可以打开同一个文件两次。你确实得到了一个新的描述符。
  • 这是一半错误,一半无关紧要。问题不是问如何让fopen 拒绝打开现有文件,而是为什么它不拒绝打开已经打开的文件。 open 调用不返回-1,不是因为文件已经打开,而是因为文件是否已经打开并不重要:如果你调用open 两次,你得到两个文件描述符。
  • C11 还引入了x 标志,用于对文件进行独占写入访问,因此wx 如果文件存在(或无法以其他方式创建,例如目录没有写入权限)将失败,否则,它将“在底层系统支持独占访问的范围内”创建具有独占访问权限的文件。我不确定它是否受到很多支持,所以我现在会坚持使用 POSIX 东西来锁定文件。无论如何,它要灵活得多。
【解决方案2】:

多次打开同一个文件是完全有效的。你提到了手册页和fcntl.h,所以我认为你正在研究一些 unix 变体。当应用程序打开文件进行写入时,Unix 不会自动锁定文件。如果程序A和程序B打开同一个文件,它们的修改会互相覆盖;这很有用,例如,当他们写入文件的不同部分时。在您的代码中,程序 A 和程序 B 是同一个进程。

两次打开同一个文件并不是特例。每次调用open 都会为您提供一个文件描述符,并且每个文件描述符都有自己的位置。每次调用fopen 都会为您提供一个文件描述符和一个 stdio 写入缓冲区。在您的第一个示例中,您将一小段数据写入每个文件,fwrite 调用将数据存储在内存中的写入缓冲区中;在调用fflush() 之前,数据实际上并未写入文件。您没有显式调用fflush(),也没有在后台调用fclose()(或达到相同效果)的fclose()fclose() 在您的程序退出时被隐式调用,但无法保证文件关闭的顺序,因此有两种可能性,您会看到第二种:

  • fp1 先关闭,导致"hello" 写入位置0。然后fp2 关闭,导致" world" 写入位置0。这将覆盖通过fp1 写入的5 个字节。
  • fp2 首先关闭,导致" world" 写入位置0。然后fp2 关闭,导致"hello" 写入位置0。这将覆盖通过fp1 写入的前5 个字节,留下@ 987654344@.

大多数 unix 系统仅通过 lockffcntl 等函数提供协作锁。如果两个程序在同一个文件上调用lockf(fd, F_LOCK, size)(不一定通过同一个文件描述符),那么第二个这样做的程序将阻塞,直到第一个释放它的锁。任何程序仍然可以通过调用write 来修改文件。一些 unix 变体(例如 Linux 确实提供强制锁,这确实会影响程序,无论它们是否知道锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 2011-05-28
    • 2014-07-09
    • 2022-01-16
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多