有人能解释一下管道缓冲区死锁吗?

18 投票
2 回答
7244 浏览
提问于 2025-04-15 20:01

Python文档中提到关于Popen的内容:

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

现在,我想弄明白这种死锁是怎么发生的,以及为什么会发生。

我的理解是:子进程会向标准输出(stdout)或标准错误(stderr)输出一些内容,这些内容会先被暂时存储在一个缓冲区中。当这个缓冲区满了之后,内容就会被发送到子进程的标准输出或标准错误中,然后通过管道传递给父进程。

根据文档的说法,管道有自己的缓冲区,当这个缓冲区满了或者子进程结束时,内容才会被发送到父进程。

不管是有管道缓冲区还是没有,我并不完全明白死锁是怎么发生的。我能想到的唯一情况是某种“全局”的操作系统管道缓冲区,进程们会争抢这个缓冲区,这听起来有点奇怪。还有一种可能是多个进程共享同一个管道,这种情况本身是不应该发生的。

有人能帮我解释一下吗?

2 个回答

5

死锁会发生在两个缓冲区(标准输入和标准输出)都满的时候:你的程序在等着往外部程序写更多的输入,而外部程序则在等着你先从它的输出缓冲区读取数据。

解决这个问题的方法是使用非阻塞输入输出,并合理安排缓冲区的优先级。你可以尝试自己去实现这个功能,但其实communicate()这个方法已经为你做好了这些事情。

10

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

我的理解是:子进程会向标准输出(stdout)或标准错误(stderr)发送一些东西,这些内容会先被暂时存储在一个缓冲区里。当这个缓冲区满了之后,内容才会被发送到子进程的标准输出或标准错误,然后通过管道传递给父进程。

这个缓冲区是父进程和子进程共享的。

子进程向标准输出发送内容,而这个标准输出的缓冲区正是父进程应该读取的地方。

当缓冲区满了,写入操作会停止,直到缓冲区被清空。对于管道来说,"刷新"并没有什么意义,因为两个进程共享同一个缓冲区。

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

撰写回答