Python 更换壳体管道

Python 更换壳体管道,python,python-2.7,Python,Python 2.7,在子流程模块的Python 2.7文档中,我发现了以下代码片段: 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()

在子流程模块的Python 2.7文档中,我发现了以下代码片段:

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()#如果p2退出,允许p1接收SIGPIPE。


这里p1.stdout正在关闭。如果p2退出,它如何允许p1接收SIGPIPE?

如果进程试图写入没有活动进程正在查看的管道,通常会发送SIGPIPE信号。在与代码段等效的shell管道中:

`dmesg | grep hda`
如果
grep
进程由于某种原因在
dmesg
完成写入输出之前终止,
dmesg
将收到一个SIGPIPE并自行终止。这将是UNIX/Linux进程()的预期行为

相反,在使用
subprocess
的Python实现中,如果
p2
p1
生成输出之前退出,SIGPIPE不会被发送,因为实际上仍然有一个进程在查看管道——Python脚本本身(创建
p1
p2
)。更重要的是,脚本正在查看管道,但不使用其内容-其效果是管道无限期地保持打开状态,
p1
陷入了困境

显式关闭
p1.stdout
将Python脚本从管道中分离出来,并使其成为除
p2
以外的任何进程都不会查看管道-这样,如果
p2
确实在
p1
之前结束,
p1
正确地获得结束自身的信号,而无需任何人为地保持管道打开

以下是另一种措辞的解释:

来自:

当进程试图写入未连接到另一端的进程的管道时,SIGPIPE信号被发送到进程。使用
stdin=p1.stdout
创建p2时,有两个进程连接到管道
p1.stdout
:父python进程和p2。即使p2提前关闭,父进程仍在运行,因此不会发送SIGPIPE信号
p1.stdout.close()
关闭父/调用方进程中的
p1.stdout
,从而使dmesg成为打开该文件描述符的唯一进程

换句话说,如果没有
p1.stdout.close()

  • p1.stdout
    在父进程中保持打开状态。如果p2退出(即存在 没有人读p1.stdout),p1不会知道没有人读
    p1.stdout
    并将继续写入
    p1.stdout
    ,直到 相应的操作系统管道缓冲区已满
  • 如果p2过早退出,
    p1.stdout
    仍将是 在父进程中打开,因此不会生成SIGPIPE

    • 一个更系统的解释:

      • 管道是由操作系统管理的实例。它有一个读端和一个写端
      • 两端可以由多个进程打开。不过,仍然只有一根管子。也就是说,多个进程可以共享同一个管道
      • 打开其中一端的进程拥有相应的文件句柄。进程可以主动
        close()
        再次关闭它!如果进程退出,操作系统将为您关闭相应的文件句柄
      • 所有涉及的进程都可以
        close()
        它们代表管道读取端的文件句柄。这没什么错,这是一个完美的局面
      • 现在,如果进程将数据写入管道的写入端,而读取端不再打开(没有进程持有读取端的打开文件句柄),符合POSIX的操作系统会向写入进程发送一个
        SIGPIPE
        信号,让它知道不再有读卡器
      这是标准机制,通过该机制,接收程序可以隐式地告诉发送程序它已停止读取。你有没有想过

      cat bigfile | head -n5
      
      实际上读取整个大文件?不,它没有,因为
      cat
      head
      退出时(从stdin读取5行后)立即检索
      SIGPIPE
      信号。需要注意的一点是:
      cat
      被设计为实际响应
      SIGPIPE
      (这是一个重要的工程决策;):它停止读取文件并退出。其他程序被设计为忽略SIGPIPE(有意地,它们自己处理这种情况——这在网络应用程序中很常见)

      如果在控制过程中保持管道的读取端打开,则禁用所述机制
      dmesg
      将无法注意到
      grep
      已退出


      然而,你的例子实际上不是一个好例子<代码>grep hda将读取整个输入
      dmesg
      是首先退出的进程。

      “没有活动进程正在查看的管道”和“仍然是正在查看管道的流程”并不是真正精确的措辞。
      cat bigfile | head -n5