我正在阅读subprocess模块部分中关于Popen类的Python文档,遇到了以下代码:
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
documentation还声明
启动P2之后的P1.StdOut.Couter()调用对于P1接收P1之前如果P2退出时是重要的。
为什么我们必须在接收到一个字节之前关闭它呢?如果我们已经关闭了,P1怎么知道P2在P1之前退出呢?
最佳答案
SIGPIPE
是一个信号,如果dmesg
试图写入一个封闭的管道,则会发送该信号。在这里,dmesg
最后有两个要写入的目标:Python进程和grep
进程。
这是因为subprocess
克隆了文件句柄(使用os.dup2()
function)。将p2
配置为使用p1.stdout
将触发一个os.dup2()
调用,该调用要求操作系统复制管道文件句柄;复制用于将dmesg
连接到grep
。
对于dmesg
stdout的两个打开的文件句柄,如果只有一个提前关闭,dmesg
将永远不会收到SIGPIPE
信号,因此不会检测到grep
关闭。dmesg
将不必要地继续产生产出。
因此,通过立即关闭p1.stdout
,您可以确保从dmesg
STDUT中剩下的唯一文件句柄是grep
进程,如果该进程退出,dmesg
接收SIGPIPE
。