【问题标题】:Python subprocess reading process terminates before writing process example, clarification neededPython子进程读取进程在编写进程示例之前终止,需要澄清
【发布时间】:2013-09-24 04:37:10
【问题描述】:

代码 sn-p 来自: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 是不是有风险?这是如何工作的?

【问题讨论】:

标签: python


【解决方案1】:

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

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

【讨论】:

  • 老鼠!我不得不写一个很长的答案并且输了。你的也不错(微笑)。
【解决方案2】:

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

尽管进程执行在 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)

创建 pipe_2。为 grep 提供 pipe_2 的写句柄和 pipe_1 的读句柄的副本。您现在有 2 个管道和 5 个句柄(dmesg 中的 pipe_1 写入,grep 中的 pipe_1 读取和 pipe_2 写入,父级中的 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... 你得到了一个管道!

【讨论】:

  • 对我来说没有意义。你是什​​么意思:“孩子的阅读结束重复”?你能谈谈 p1 stdout 和 p2 stdin 吗?
  • 对不起,我会扩大答案。
猜你喜欢
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多