考虑一个父程序启动了一个子程序,并且父项的标准输出附加到子项的标准输入,并且子项的标准输出附加到父项的标准输入。
stdin <- stdout
parent child
stdout -> stdin
如果孩子(异步)不断读取其标准输入并将数据写入其标准输出,但父母只是写入孩子的标准输入并且根本没有读取孩子的标准输出:
stdin| << stdout
parent child
stdout ==>==> stdin
最终会出现堵塞吗?标准输入和标准输出是否共享任何类型的缓冲区?特别是通过C ++ std::cin
(istream)和std::cout
(ostream),如果需要回答的话。标准是否要求他们做或不做这样的事情,还是将其留给实施?
会发生什么?
答案 0 :(得分:7)
您不能将文件描述符从进程“附加”到不同进程的文件描述符。你所做的(如果你的操作系统支持它)是将两个文件描述符分配给“管道”的末尾。管道未在C / C ++标准中的任何地方指定(它们由POSIX定义),并且您将找不到任何标准的C / C ++库函数,它们完全引用它们。
由Unix(和类似Unix)系统实现,管道只不过是操作系统中的某个缓冲区。缓冲区未满时,进程可以将数据写入管道的输入端;数据只是添加到缓冲区。当缓冲区不为空时,进程可以从缓冲区的输出端读取数据;数据从缓冲区中删除并传递给读取过程。如果进程尝试写入缓冲区已满的管道或从缓冲区为空的管道读取,则进程“阻塞”:即,内核调度程序将其标记为不可运行,并且它将保持该状态,直到管道可以处理它的请求。
问题中描述的场景需要涉及两个管道。一个管道用于允许父项的stdout将数据发送到子项的stdin,另一个管道用于允许子项的stdout将数据发送到父项的stdin。这两根管子完全相互独立。
现在,如果父级停止从其stdin读取,但子级继续写入其stdout,则最终管道缓冲区将变满。 (它实际上不会花很长时间。管道缓冲区不是很大,而且它们不会增长。)此时,孩子将阻止尝试写入管道。如果孩子没有多线程,那么一旦它阻塞,那就是它。它停止运行,因此它不再从stdin读取。如果孩子停止从其标准输入读取,那么另一个管道将很快变满,父母也会阻止尝试写入标准输出。
因此,不需要共享资源以实现死锁。
这是一个非常着名的过程,它会产生一个孩子,并在阅读孩子的反应时尝试向孩子提供数据。如果读者没有跟上产生的数据,那么很可能会出现死锁。您可以通过搜索“管道缓冲区死锁”来找到有关它的大量信息。以下是一些示例链接,只是随机的:
Raymond Chen,MSDN:http://blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx
就在StackOverflow上(参考Python,但问题是相同的):Can someone explain pipe buffer deadlock?
David Glasser,自2006年起:http://web.mit.edu/6.033/2006/wwwdocs/writing-samples/unix-DavidGlasser.html(“这些限制不仅仅是理论上的 - 他们可以在实践中看到,以后在Unix中没有形成任何主要的进程间通信形式。管“)