我有一个从stdin读取的程序,在执行此操作时,有时需要fork()并异步执行命令。有时在此fork之后不久(可能在命令运行时),父级似乎丢失了从stdin读取的数据。如果只丢失一个字节,程序将永远不会恢复。
fork()的手册页说子进程继承了父进程打开文件描述符的副本,但是在fork()之后和execvp()之前尝试close(STDIN_FILENO)没有帮助。
我的程序基本上是这样做的:
if (we_need_to_run_a_command_asynchronously) {
if (!fork()) {
close(STDIN_FILENO);
char *args[] = { "command", "arg", "etc", 0 };
setpriority(PRIO_PROCESS, 0, -19);
execvp("command", args);
exit(0); // Ignore errors
}
}
我怎样才能确定孩子不会干扰父母的标准差(或属于父母的任何其他东西)?执行的命令通常是shell脚本。
我不知道我的程序到底出了什么问题,但只要不运行任何命令就可以正常运行。只有在运行命令后有时(并不总是),它从stdin读取的数据会以某种方式被破坏,这就是为什么我怀疑问题与fork()之后的stdin有关。
答案 0 :(得分:3)
您可以打开另一个文件并使用dup2将新文件描述符复制到stdin上,而不是关闭stdin。如果您打开/dev/null
并在fork
之后以及exec
之前将其重定向到标准输入,则该儿童不会干扰父母的标准输入。
答案 1 :(得分:2)
我想我已经解决了这个问题。事实证明,在我的程序很少采用的路径中也执行了命令,我忘了在fork()之后对子进程中的stdin做任何事情。这段代码确实有效:
int pid = fork();
if (!pid) {
int fd = open("/dev/null", O_RDWR);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2)
close(fd);
execvp(...);
所以,我的错误,但也许我的问题仍然可以作为一个重要的提醒,当分叉执行一个应该与父进程分开运行的命令时,通常需要使用stdin / stdout / stderr做一些事情。我认为上述方法是一种干净的方式。
答案 2 :(得分:0)
我在execve()
的POSIX规范中找到了一个有趣的注释
和相关的功能:
如果在成功调用exec系列函数之后文件描述符0,1或2将被关闭,则实现可以在新过程映像中打开文件描述符的未指定文件。如果执行标准实用程序或符合要求的应用程序且文件描述符0未打开以供读取或文件描述符1或2未打开以进行写入,则执行该实用程序或应用程序的环境应视为不符合要求,因此实用程序或应用程序的行为可能与本标准中描述的不同。
有趣的是推测您的实施是否正在行使'可能'选项。明确打开/dev/null
作为标准输入是最安全的。