使用跨叉继承的 stdio.h FILE * 流有哪些缺陷?

What are the pitfalls of using a stdio.h FILE * stream inherited across a fork?

我写了一个(简单的)包装器在子进程中执行另一个进程 过程。子进程在调用 exec() 之前关闭(或重定向)标准错误。但是,如果 exec() 失败,我希望在父进程的标准错误上打印一条错误消息。

以下是我目前的解决方案。我在 fork 之前 dup()licate 标准错误。 因为我希望能够格式化 I/O,所以我在重复的对象上调用了 fdopen() 处理。 (我知道,就目前而言,这个程序毫无意义,但我有 只保留了我问题的相关部分。)

#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int status;
    FILE *log = fdopen(dup(2), "w");
    switch (fork()) {
        case -1:
            fprintf(log, "fork: %s\n", strerror(errno));
            break;
        case 0:
            close(2);
            execvp(argv[1], &argv[1]);
            fprintf(log, "execvp: %s\n", strerror(errno));
            break;
        default:
            wait(&status);
            break;
    }
    return 0;
}

我现在的问题是:使用FILE *变量的陷阱是什么log, 在父进程和子进程中?我读过 IEEE Std 1003.1-2008 标准、XSH、Sections 2.5 and 2.5.1 ("Interaction of File Descriptors and Standard I/O Streams"),据我所知,这应该可行,但如果发生标准错误,我可能需要在 fork 之前添加 fflush(log)被缓冲。那是对的吗?还有其他需要注意的陷阱吗?

此外,同一标准在第 2.5 节中指出

The address of the FILE object used to control a stream may be significant; a copy of a FILE object need not necessarily serve in place of the original.

我可以认为这意味着通过 fork() 继承的 FILE 对象是安全的吗? 使用?

我应该补充一点,我不打算在父进程之间做任何事情 fork() 和 wait()。

除了fork前需要fflush,你还应该考虑标准错误可能不是console/pipe,而是一个普通文件的情况。

如果您从两个文件描述符(这就是您的 parent 和 child 现在拥有的)写入常规文件,写入可能会相互重叠。如果发生这种情况,您可能会丢失信息。

我建议使用 fcntl(F_SETFL) 来确保文件描述符处于 O_APPEND 模式。在那种模式下,两个进程可以写入同一个文件而不会相互覆盖。

即使在 O_APPEND 中,如果写入太大,输出也可能重叠。由于您使用的是缓冲 IO(FILE* 是什么),因此这种可能性较小,但并非不可能。在这一点上,你最好的选择就是抓住这个机会,并且相对频繁地 fflush,这样发送到 write 的缓冲区就不会太大。