我正在阅读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
对于dmesgstdout的两个打开的文件句柄,如果只有一个提前关闭,dmesg将永远不会收到SIGPIPE信号,因此不会检测到grep关闭。dmesg将不必要地继续产生产出。
因此,通过立即关闭p1.stdout,您可以确保从dmesg STDUT中剩下的唯一文件句柄是grep进程,如果该进程退出,dmesg接收SIGPIPE

09-06 08:30