gpt4 book ai didi

当 fork() 遇到 fopen() 时的困惑

转载 作者:行者123 更新时间:2023-12-02 03:04:52 24 4
gpt4 key购买 nike

我发现如果我们在关闭文件流之前执行 fork() 操作,打开的文件流将会变得困惑。众所周知,当父进程和子进程想要修改文件流时,可能会发生并发,即竞争条件。但是,即使子进程从未接触过文件流,它仍然具有未定义的行为。我想知道是否有人可以从内核在子进程 fork 和退出阶段如何处理文件流来解释这一点。

下面是一个奇怪行为的快速片段:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
// Open file
FILE* fp = fopen("test.txt", "r");

int count = 0;
char* buffer = NULL;
size_t capacity = 0;
ssize_t line = 0;
while ( (line = getline(&buffer, &capacity, fp)) != -1 ) {
if (line > 0 && buffer[line - 1] == '\n') // remove the end '\n'
buffer[line - 1] = 0;

pid_t pid = fork();
if (pid == 0) {
// fclose(fp); // Magic line here: when you add this, everything is fine
if (*buffer == '2')
execlp("xyz", "xyz", NULL);
else
execlp("pwd", "pwd", NULL);
exit(1);
} else {
waitpid(pid, NULL, 0);
}
count++;
}
printf("Loops: %d\n", count);
return 0;
}

只需将代码复制到新文件中(例如 test.c)。并创建一个包含简单内容的 .txt 文件 test.txt

1
2
3
4

然后运行

$ gcc test.c && ./a.out

文件中有 4 行。循环预计读取每一行并执行 4 次 (1 2 3 4)。我选择让它在第二个循环中执行无效命令“xyz”。然后,你会发现循环实际上执行了6次(1 2 3 4 3 4)!事实是,当执行的所有四个命令都有效时,不会出现任何问题。但如果执行了无效命令,则该命令之后的每个命令都会被执行两次。 (请注意,这种奇怪的行为仅发生在 Linux 机器上,我的 Mac 操作系统运行良好,不确定 Windows 是否如此。所以问题与平台相关?)

看起来每当我 fork() 时,父进程中的文件流不再被 promise 为旧的 fp(非确定性行为),即使我的子进程不触及它。

我找到的临时解决方案是:子进程中的 fclose(fp) 。这将使上述奇怪的行为消失,但在更复杂的条件下,仍然可以观察到其他事情。如果有人能给我一些关于这个问题的见解,我将不胜感激。谢谢

最佳答案

正如评论中所述,您需要在调用 exec 之前关闭打开的文件描述符。

this blogpost (第 4 节)有一个简洁的代码示例,您可以使用它来确保所有 fd 都关闭,即使在您并不总是知道当前打开了哪些文件的复杂应用程序中:

for ( i=getdtablesize(); i>2; --i) 
close(i); /* close all descriptors */

(稍微修改以保持标准输入、标准输出、标准错误打开)

这有点hacky,但它确实有效。如果您想避免这种情况,还可以在打开的每个文件描述符上设置 O_CLOEXEC 标志。由于使用 fopen 时,您不直接调用 open(),因此您可以通过向其添加 'e' 标志来完成此操作(当使用 glibc >= 2.7 时):

FILE* fp = fopen("test.txt", "er");

调用 exec*() 时,所有带有此标志的文件描述符都会自动关闭。

关于当 fork() 遇到 fopen() 时的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52562614/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com