在什么情况下,Python子进程会获得SIGPIPE?


问题内容

我正在子过程模块部分中阅读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]

文档还指出

“启动p2之后的p1.stdout.close()调用很重要,如果p2在p1之前退出,则p1会收到SIGPIPE。

为什么在我们收到SIGPIPE之前必须先关闭p1.stdout,如果已经关闭p1,p1如何知道p2在p1之前退出?


问题答案:

SIGPIPE是一个信号,如果dmesg尝试写入关闭的管道将被发送。在这里,dmesg最后有 两个
要写入的目标,即Python进程和该grep进程。

那是因为subprocess克隆文件句柄(使用os.dup2()函数)。配置p2为使用p1.stdout会触发一个os.dup2()调用,要求操作系统复制管道文件句柄。重复用于连接dmesggrep

对于dmesgstdout,有两个打开的文件句柄,如果只有 其中一个
提前关闭,dmesg则永远不会发出SIGPIPE信号,因此永远不会检测到关闭。会不必要地继续产生产出。 __grep``dmesg

因此,通过p1.stdout立即关闭,可以确保从dmesgstdout读取的唯一剩余文件句柄是该grep进程,并且如果该进程将退出,则会dmesg收到SIGPIPE