【问题标题】:Do stdin and stdout share any resources?标准输入和标准输出是否共享任何资源?
【发布时间】:2015-10-08 06:57:39
【问题描述】:

标准输入和标准输出是否独立?

假设父程序启动了一个子程序,父程序的标准输出附加到子程序的标准输入,子程序的标准输出附加到父程序的标准输入。

        stdin       <- stdout 
parent                        child
        stdout ->      stdin

如果子进程(异步)不断地从其标准输入读取数据并将数据写入其标准输出,但父进程只是向子进程的标准输入写入数据,而根本没有从子进程的标准输出读取数据:

        stdin|     << stdout 
parent                       child
        stdout ==>==> stdin

最终会出现阻塞吗?标准输入和标准输出是否共享任何类型的缓冲区?如果需要回答,特别是通过 C++ std::cin (istream) 和 std::cout (ostream)。标准是否要求他们共享或不共享此类内容,还是由实施决定?

会发生什么?

【问题讨论】:

  • 当您谈论重定向和阻塞时,您应该使用它们的 Unix 名称 stdin 和 stdout 来引用这些流,而不是使用它们的 C++ 名称。
  • 他们为什么要共享一个缓冲区?
  • @JonathonReinhart tyvm,已编辑。
  • 我建议流的语言无关名称是“标准输入”和“标准输出”。名称stdin 是标准输入文件流的C 语言标识符,stdout 是标准输出文件流的C 语言名称。这些对应于 C++ 中的类似名称:stdin 对应于 &lt;cstdio&gt; 中的 std::stdin(我认为,&lt;stdio.h&gt; 中的 ::stdin),也对应于 std::cin,尽管 I/O 函数与std::cinstd::stdout 使用的有很大不同;和stdout 对应于std::stdout (::stdout) 和std::cout

标签: c++ terminal stdout stdin piping


【解决方案1】:

您不能将文件描述符从进程“附加”到不同进程的文件描述符。您所做的(如果您的操作系统支持它)是将两个文件描述符分配给“管道”的末端。管道在 C/C++ 标准中的任何地方都没有指定(它们由 POSIX 定义),而且您根本找不到任何标准的 C/C++ 库函数对它们进行任何引用。

正如 Unix(和类 Unix)系统所实现的那样,管道只不过是操作系统中某处的缓冲区。当缓冲区未满时,进程可以将数据写入管道的输入端;数据只是简单地添加到缓冲区中。当缓冲区不为空时,进程可以从缓冲区的输出端读取数据;数据从缓冲区中删除并移交给读取过程。如果一个进程试图写入缓冲区已满的管道或从缓冲区为空的管道读取,则该进程“阻塞”:也就是说,它被内核调度程序标记为不可运行,并且它保持在该状态直到管道可以处理它的请求。

问题中描述的场景需要涉及两个管道。一个管道用于允许父级的标准输出向子级的标准输入发送数据,另一个用于允许子级的标准输出向父级的标准输入发送数据。这两个管道完全相互独立。

现在,如果父级停止从其标准输入读取,但子级继续写入其标准输出,那么最终管道缓冲区将变满。 (实际上不会花费很长时间。管道缓冲区不是很大,而且它们不会增长。)此时,孩子将阻止尝试写入管道。如果孩子不是多线程的,那么一旦它阻塞,就是这样。它停止运行,因此不再从其标准输入中读取。如果子进程停止从它的标准输入读取,那么另一个管道很快就会变满,父进程也会阻止尝试写入它的标准输出。

因此,没有必要为了实现死锁而共享资源。

这是一个非常著名的错误,它会生成一个子进程并在读取子进程的响应时尝试向该子进程提供数据。如果阅读器跟不上产生的数据,则很可能出现死锁。例如,通过搜索“管道缓冲区死锁”,您会找到很多有关它的信息。以下是一些示例链接,只是随机的:

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 中开发的主要进程间通信形式都不是在管道之上分层的。 ")

【讨论】:

  • 是的,管道。我什至没有意识到那是它。我实际上正在考虑我正在编写的一个特定程序,但是 API 非常高级,所以当我做这样的事情时,我并没有很明显地使用管道。哦。 ----- 非常详细的回答!非常感谢。 ----- 不,根本不是理论上的,事实上我不得不根据这个答案重构父程序以避免死锁。虽然我很欣赏你的回应,但它现在可以工作了。
猜你喜欢
  • 2014-12-28
  • 2021-10-20
  • 2013-09-18
  • 1970-01-01
  • 2013-06-13
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
  • 2014-07-22
相关资源
最近更新 更多