有人可以解释管道缓冲区死锁吗?

Posted

技术标签:

【中文标题】有人可以解释管道缓冲区死锁吗?【英文标题】:Can someone explain pipe buffer deadlock? 【发布时间】:2011-01-23 19:35:38 【问题描述】:

Popen 状态的 Python 文档:

警告使用communicate() 而不是.stdin.write、.stdout.read 或.stderr.read 以避免由于任何其他操作系统管道缓冲区填满并阻塞子进程而导致的死锁。

现在,我正试图弄清楚这种死锁是如何发生的以及为什么会发生。

我的心智模型:子进程向stdout/err产生一些东西,它被缓冲,缓冲区被填满后,它被刷新到子进程的stdout/err,通过管道发送到父进程。

根据文档所述,管道有自己的缓冲区,当它被填充或子进程终止时,它会被刷新到父进程。

无论哪种方式(是否使用管道缓冲区),我都不完全确定死锁是如何发生的。我唯一能想到的是某种“全局”操作系统管道缓冲区进程将努力争取,这听起来很奇怪。另一个是更多的进程将共享同一个管道,这不应该单独发生。

有人可以解释一下吗?

【问题讨论】:

【参考方案1】:

小心,这里面有一个微妙的错误。

我的心智模型:子进程产生 标准输出/错误的东西,这是 缓冲并在缓冲区被填充后, 它被刷新到标准输出/错误 子进程,通过 管道到父进程。

缓冲区由父进程和子进程共享。

子进程向标准输出产生一些东西,这与父进程应该从中读取的缓冲区相同。

当缓冲区被填满时,写入停止,直到缓冲区被清空。 Flush 对管道没有任何意义,因为两个进程共享同一个缓冲区。

刷新到磁盘意味着设备驱动程序必须将字节下推到设备。刷新套接字意味着告诉 TCP/IP 停止等待累积缓冲区并发送内容。刷新到控制台意味着停止等待换行并将字节通过设备驱动程序推送到设备。

【讨论】:

【参考方案2】:

当两个缓冲区(stdin 和 stdout)都已满时,可能会发生死锁:您的程序正在等待向外部程序写入更多输入,而外部程序正在等待您首先从其输出缓冲区中读取。

这可以通过使用非阻塞 I/O 并适当地确定缓冲区的优先级来解决。您可以自己尝试使其工作,但 communicate() 只是为您完成。

【讨论】:

是的,但我们正在讨论从大于内存大小的数据中读取数据。但感谢您的澄清。

以上是关于有人可以解释管道缓冲区死锁吗?的主要内容,如果未能解决你的问题,请参考以下文章

有人可以解释这段代码如何使用 Accelerate Framework 将音量转换为分贝吗?

recv() 函数可以接收比其内部缓冲区更多的字节吗?

有人可以解释 Ruby 在块中使用管道字符吗?

我需要刷新命名管道吗?

可以使用管道消除 TCP 碎片吗?

为啥在同一个 goroutine 中使用无缓冲通道会导致死锁?