Python子进程读取过程在编写进程示例之前终止,需要澄清

时间:2013-09-24 04:37:10

标签: python

来自http://docs.python.org/3/library/subprocess.html#replacing-shell-pipeline

的代码段
output=`dmesg | grep hda`
# becomes
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]

问题:我不太明白为什么需要这一行:p1.stdout.close()? 如果通过这样做p1 stdout甚至在完全输出数据并且p2仍然存活之前就关闭了怎么办?这么快就关闭p1.stdout我们不是冒险吗?这是如何工作的?

2 个答案:

答案 0 :(得分:2)

p1.stdout.close()关闭文件描述符的Python的副本p2已打开该描述符(通过stdin=p1.stdout),因此关闭Python的描述符不会影响p2。但是,现在管道端只打开一次,所以当它关闭时(例如,如果p2死亡),p1将看到管道关闭并将获得SIGPIPE

如果你没有在Python中关闭p1.stdout,并且p2死了,那么p1将没有信号,因为Python的描述符将保持管道打开。

答案 1 :(得分:1)

管道是进程外部的(它是操作系统的东西),并且由使用读写句柄的进程访问。许多进程可以拥有管道的句柄,如果管理不当,可以以各种灾难性的方式进行读写。当管道的所有手柄都关闭时,管道关闭。

虽然在Linux和Windows中流程执行的工作方式不同,但基本上会发生这种情况(我会被杀死!)

p1 = Popen(["dmesg"], stdout=PIPE)

创建pipe_1,将dmesg的写句柄作为其标准输出,并将父句中的读句柄返回为p1.stdout。您现在有一个带有2个句柄的管道(pipe_1在dmesg中写入,pipe_1在父项中读取)。

p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)

创建pipe_2。为grep提供pipe_2的写入句柄和pipe_1的读取句柄的副本。您现在有2个管道和5个句柄(pipe_1在dmesg中写入,pipe_1读取和pipe_2在grep中写入,pipe_1读取和pipe_2在父级中读取)。

p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.

请注意,pipe_1有两个读句柄。您希望grep具有读取句柄,以便它读取dmesg数据。您不再需要父级中的句柄。关闭它,以便pipe_1上只有1个读取句柄。如果grep死掉,它的pipe_1读取句柄被关闭,操作系统注意到pipe_1没有剩余的读取句柄,并给dmesg带来了坏消息。

output = p2.communicate()[0]

dmesg将数据发送到stdout(pipe_1写入句柄),开始填充pipe_1。 grep读取清空pipe_1的stdin(pipe_1读取句柄)。 grep还写入stdout(pipe_2写句柄)填充pipe_2。父进程读取pipe_2 ...你得到了一个管道!