【问题标题】:Why can a subprocess still write to stdout after it's been closed?为什么子进程关闭后仍然可以写入标准输出?
【发布时间】:2018-09-18 01:39:00
【问题描述】:

我在subprocess documentation 中找到了这段代码,其中一个进程的标准输出正在通过管道传输到另一个进程:

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]

我对那个stdout.close() 电话感到困惑。确定关闭标准输出句柄会阻止进程产生任何输出吗?

所以我进行了一个实验,令我惊讶的是,这个过程根本没有受到影响:

from subprocess import Popen, PIPE

p1 = Popen(['python', '-c', 'import time; time.sleep(5); print(1)'], stdout=PIPE)
p2 = Popen(['python', '-c', 'print(input()*3)'], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
print('stdout closed')
print('stdout:', p2.communicate()[0])

# output:
# stdout closed
# [5 second pause]
# stdout: b'111\n'

这里发生了什么?为什么进程可以写入封闭管道?

【问题讨论】:

  • p1.stdout 是 Python 对象 连接dmesg 的标准输出;它不是dmesg 实际使用的文件描述符。同样,grep 的标准输入在您尝试关闭它之前已经获得了自己的文件句柄副本。
  • @chepner 我想这是有道理的,但是如果我们关闭输出管道又有什么关系呢?如果这只是一个副本,为什么关闭它会允许进程接收 SIGPIPE?
  • 它没有;如果dmesggrep 关闭后尝试写入其管道末端,则它会获得一个 SIGPIPE。在正常操作下,dmesg 写入它需要的内容,然后正常退出。另一方面,grep 保持打开状态,直到它看到它正在 读取 的管道已关闭,然后正常退出。除非grep 提前终止,并且在dmesg 完成写入输出之前,否则不会发送SIGPIPE
  • 简而言之,管道、进程和信号令人困惑。明白了。有一半没看懂,不过还是很感谢你的解释,谢谢。我想我有一些关于维基百科的阅读要做。
  • 我没有发布答案的原因是我几乎可以肯定这其中有一些微妙的方面我没有正确解释。 :)

标签: python subprocess pipe


【解决方案1】:

关闭文件描述符仅意味着减少引用计数(在操作系统内核内部)。描述符编号变得无效,但除非引用计数达到零,否则它所引用的对象不会发生任何事情。

Popen 调用中,正在执行复制文件描述符的操作,从而增加引用计数:像dup/dup2fork 这样的操作。

如果我们fork 子进程从父进程接收文件描述符,则该文件描述符是指向同一对象的副本。

如果父级关闭了该描述符的原始副本,则不会影响子级中的那个。反之亦然。只有当父和子都关闭该描述符时,底层打开的文件/设备对象才会消失;并且只有在没有其他描述符引用它的情况下。

即使描述符具有相同的编号也是如此;每个进程都有自己的文件描述符编号表。子文件描述符 3 与父文件描述符 3 不同。

【讨论】:

    【解决方案2】:

    每个进程都有自己的一组文件描述符。您可以(并且应该)在不需要父副本时关闭它;实际管道(端点)一直存在,直到 last 进程(大概是p2)关闭它。只有这样,尝试写入管道(另一端)的进程才会得到SIGPIPE

    如果您不关闭它,即使在p2 退出后, 仍可能会读取它,这将不必要地保持p1 活着(如果父 waits 就可以了)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-01
      • 1970-01-01
      • 2013-04-02
      • 2013-11-06
      • 1970-01-01
      • 2019-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多