如何防止孩子在 fork() 之后干扰父母的标准输入
Posted
技术标签:
【中文标题】如何防止孩子在 fork() 之后干扰父母的标准输入【英文标题】:How to prevent child from interfering with parent's stdin after fork() 【发布时间】:2015-06-12 19:22:16 【问题描述】:我有一个从标准输入读取的程序,在执行此操作时,它有时需要 fork() 并异步执行命令。有时在此分叉后不久(可能在命令运行时),父级似乎丢失了从标准输入读取的数据。如果只丢失一个字节,程序将永远无法恢复。
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 脚本。
我不知道我的程序到底出了什么问题,但只要它不运行任何命令,它就可以正常运行。只有有时(并非总是)在它运行命令之后,它从标准输入读取的数据以某种方式被破坏,这就是为什么我怀疑问题与 fork() 之后的标准输入有关。
【问题讨论】:
您能否通过编写两个小型测试程序来检验您的假设,一个从标准输入读取,一个不读取,然后让您的父程序执行这些程序,看看会发生什么? 请分享一个可编译的小程序。它可以帮助我们进入它 您可以为标准输入设置 close-on-exec 标志,而不是显式关闭。但是,问题可能有不同的原因(演示问题的简短示例程序可能会有所帮助)。 也许可以产生一个 pthread 而不是一个新进程? 您执行的命令有权打开标准输入。你可以给它/dev/null
来阅读。您对command
了解多少(除了名称command
是一个内置的shell)?你写了吗?它是从什么读来的?基本上,如果您将其标准输入连接到/dev/null
,您应该不会看到问题。
【参考方案1】:
您可以打开另一个文件并使用dup2 将新文件描述符复制到标准输入上,而不是关闭标准输入。如果您打开/dev/null
并在fork
之后和之前以及exec
之前将其重定向到标准输入,则孩子不会干扰父母的标准输入。
【讨论】:
/dev/null
是比 /dev/zero
更传统的选择,这是有充分理由的。【参考方案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 执行某些操作。我认为上述方式是一种干净的方式。
【讨论】:
【参考方案3】:我在execve()
的 POSIX 规范中发现了一个有趣的注释
及相关功能:
如果文件描述符 0、1 或 2 在成功调用 exec 系列函数之一后将被关闭,则实现可能会为新进程映像中的文件描述符打开一个未指定的文件。如果执行标准实用程序或符合标准的应用程序时文件描述符 0 未打开以供读取或文件描述符 1 或 2 未打开以供写入,则执行实用程序或应用程序的环境应被视为不符合标准,因此实用程序或应用程序的行为可能与本标准中所述不同。
推测您的实现是否正在执行“可能”选项很有趣。显式打开/dev/null
作为标准输入是最安全的。
【讨论】:
以上是关于如何防止孩子在 fork() 之后干扰父母的标准输入的主要内容,如果未能解决你的问题,请参考以下文章