所以我试图在fork()之后在子进程中运行系统命令(或exec,或其他),并向其推送一些输入,然后获取其输出。在fork()之后它看起来像这样,而pc和cp是父子管和子父管。
case 0:
/* Child. */
close(STDOUT_FILENO); /* Close current stdout. */
dup2(cp[1], STDOUT_FILENO);
close(STDIN_FILENO);
dup2(pc[0], STDIN_FILENO);
close( pc[1]);
close( cp[0]);
execlp("cat", "cat", NULL);
exit(1);
default:
/* Parent. */
/* Close what we don't need. */
printf("Input to child:\n");
string theinput("Hey there baby");
write(pc[1], theinput.c_str(), theinput.size());
close(pc[1]);
cout << "The input : " << theinput << endl;
printf("\nOutput from child:\n");
close(cp[1]);
while( read(cp[0], &ch, 1) == 1)
{
write(1, &ch, 1);
outcount++;
}
exit(0);
现在,它似乎工作得很好(如果你想要代码:http://pastebin.com/Fh7GrxYm),但是当我在irc上的#posix上谈话时,他们发疯了,关于它如何可能阻塞,以及它是如何阻止的“取决于内核的感受”。
有一篇关于同一事情的msdn博客文章:http://blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx
如何阻止阻止等?
答案 0 :(得分:1)
在您的情况下,父进程到达此行时可能会发生死锁:
write(pc[1], theinput.c_str(), theinput.size());
如果“输入”是大量数据,那么父进程可能会填满pc
管道。子进程(此处cat
)可能会读取其中的一些但不是全部。 cat
他们会回复你。但是,如果它是大量数据,它可能会填满cp
管道,并将阻塞,直到有人从该管道中读取数据。这将永远不会发生,因为父流程被阻止等待pc
管道流失,并且永远不会到达消耗cp
管道内容的代码。死锁。
就像你的IRC好友说的那样,这种情况是否发生取决于许多因素,例如所涉及的数据量,管道在阻塞之前可以容纳的数据量(与内核相关的参数),由父母或子女进程执行的stdio或其他缓冲......
您的选择是:
使用两个进程来控制外部命令:一个用于提供数据,另一个用于读取结果。你必须为此fork()
两次。这看起来很像shell管道。通常,最终数据源是孙子进程,过滤器是中间父进程,以及grantparent进程的最终数据接收器。
使用两个线程来控制外部命令。与之前的选项类似。
使用非阻塞I / O来控制外部命令。使用fcntl()
将两个文件描述符设置为非阻塞模式,并使用poll()
或select()
设置事件循环,以等待任一文件描述符准备就绪。当任一文件描述符准备就绪时,请准备write()
仅部分完成,read()
不能一次读取所有内容。
使用现有的事件循环,如glib's,将管道设置为IO Channels,watch them知道何时读取或写入数据。与前一个选项类似,但使用现有框架,因此您可以与现有的应用程序事件循环集成。
BTW:您的exit(1)
应为_exit(1)
,以防止C库在短期子进程中不恰当地调用退出时挂钩。