使用跨叉继承的 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
的缓冲区就不会太大。
我写了一个(简单的)包装器在子进程中执行另一个进程 过程。子进程在调用 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
的缓冲区就不会太大。