C 使用通过fork继承的stdio.h FILE*流有哪些陷阱?

C 使用通过fork继承的stdio.h FILE*流有哪些陷阱?,c,fork,posix,system-calls,stdio,C,Fork,Posix,System Calls,Stdio,我编写了一个(过于简单的)包装器,它在子进程中执行另一个进程 过程子进程在调用exec()之前关闭(或重定向)标准错误。但是,如果exec()失败,我希望在父进程的标准错误上打印一条错误消息 下面是我目前的解决方案。我在fork之前复制标准错误。 因为我希望能够进行格式化I/O,所以我对复制的文件调用fdopen() 手柄(我知道,就目前情况而言,这个计划没有什么意义,但我知道 只保留了我问题的相关部分。) #包括 #包括 #包括 #包括 #包括 int main(int argc,char*a

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

下面是我目前的解决方案。我在fork之前复制标准错误。 因为我希望能够进行格式化I/O,所以我对复制的文件调用fdopen() 手柄(我知道,就目前情况而言,这个计划没有什么意义,但我知道 只保留了我问题的相关部分。)

#包括
#包括
#包括
#包括
#包括
int main(int argc,char*argv[])
{
智力状态;
文件*log=fdopen(dup(2),“w”);
开关(fork()){
案例1:
fprintf(日志,“fork:%s\n”,strerror(errno));
打破
案例0:
关闭(2);
execvp(argv[1],&argv[1]);
fprintf(日志,“execvp:%s\n”,strerror(errno));
打破
违约:
等待(&状态);
打破
}
返回0;
}
我现在的问题是:使用
文件*
变量
日志
,有哪些陷阱, 在父进程和子进程中?我读过IEEE标准1003.1-2008 Standard,XSH(“文件描述符和标准I/O流的交互”),据我所知,这应该是可行的,但如果碰巧缓冲了标准错误,我可能需要在fork之前添加
fflush(log)
。对吗?还有其他陷阱需要注意吗

同样,该标准在第2.5节中规定:

用于控制流的文件对象的地址可能是重要的; 文件对象的副本不必代替 原创的

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

我应该补充一点,我不打算在两人之间的家长流程中做任何事情
FoK()和WaIT().

< P>除了在Fo耙之前需要<代码> FFLUSS,还应该考虑标准错误可能不是控制台/管道,而是常规文件的情况。

如果您从两个文件描述符(您的父级和子级现在都有)写入常规文件,则写入操作可能会相互重叠。如果发生这种情况,您可能会丢失信息

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


即使在O_APPEND中,如果写入太大,输出也可能重叠。由于您使用的是缓冲IO(这就是
文件*
的含义),所以这种可能性较小,但并非不可能。在这一点上,你最好的办法是抓住这个机会,也要经常使用<代码> FFLUSS,这样发送到写< /COD>的缓冲区不会太大。

< P>除了在Fo耙之前需要<代码> FFLUSS,你还应该考虑标准错误可能不是控制台/管道,而是常规文件的情况。

如果您从两个文件描述符(您的父级和子级现在都有)写入常规文件,则写入操作可能会相互重叠。如果发生这种情况,您可能会丢失信息

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


即使在O_APPEND中,如果写入太大,输出也可能重叠。由于您使用的是缓冲IO(这就是
文件*
的含义),所以这种可能性较小,但并非不可能。在这一点上,你最好的办法就是抓住这个机会,并且相对频繁地进行
fflush
,这样发送到
write
的缓冲区就不会太大。

你可能还想看看(默认情况下,
stderr
不会被缓冲)我发现了一个我在中分析过的非常特殊的情况。TL;DR-是的,您希望
fflush
fork之前的每个共享流-包括输入流。请注意,刷新输入流的行为仅在POSIX.1-2008中指定…@Edward我的问题是关于使用文件流的安全性(或不安全性)。密切关注执行官是高度相关的<基于代码>文件的流构建在标准文件描述符之上。如果流下面的文件描述符设置了
FD_CLOEXEC
标志,则该描述符将在
exec()
上关闭,并且
文件
流将以危险的方式无效-用于
文件
的文件描述符值可用于其他用途,导致流以静默方式访问完全不同的内容。有很多原因可以解释为什么在这里避免stdio I/O和使用低级POSIX I/O是一个更好的选择。就我个人而言,在这种情况下,我在
write()
周围使用了一个非常简单的包装器(还使用
fcntl()
来标记重复的描述符
O_CLOEXEC
,这样它就不会对新的可执行文件保持打开状态)。这种方法还允许将代码移植到Mac OS X,在Mac OS X中,
fork()
exec()
之间的环境有点局限于异步信号安全函数。使用是另一种选择(在POSIX.1-2008中),用于使用低级I/O,但我不清楚是哪个C库实现了它。您可能还想看看(默认情况下,
stderr
不缓冲)太长了,读不下去了,我发现了一个非常奇怪的角落案例,我分析了。Tr.Dr-是的,你想在Fooff[#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; }