2

代码片段来自: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这么快关门是不是冒着这个风险?这是如何运作的?

4

2 回答 2

2

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

如果您没有p1.stdout在 Python 中关闭并p2死亡,p1则不会收到任何信号,因为 Python 的描述符将使管道保持打开状态。

于 2013-09-24T04:51:07.733 回答
1

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

尽管进程执行在 Linux 和 Windows 中的工作方式不同,但这基本上是发生的事情(我会为此被杀死!)

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

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

p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=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 将数据发送到开始填充 pipe_1 的标准输出(pipe_1 写入句柄)。grep 读取清空 pipe_1 的标准输入(pipe_1 读取句柄)。grep 还写入填充 pipe_2 的 stdout(pipe_2 写入句柄)。父进程读取 pipe_2... 你得到了一个管道!

于 2013-09-24T04:49:14.607 回答