2015-01-06 6 views
9

の交換、私は次のスニペットが見つかりました:は、サブプロセスモジュールの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] 

出典:https://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline

私はこの行を理解していない。ここでp1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.

P1 .stdoutが閉じられています。 p2が終了すると、p1はSIGPIPEをどのように受け取ることができますか?

+0

可能性のある[Pythonサブプロセスモジュールの例を説明します](0120-919-066)(英語) –

答えて

4

SIGPIPEシグナルは、通常、プロセスがアクティブなプロセスが見つからないパイプに書き込もうとすると送信されます。あなたのコードスニペットのシェルパイプラインと同等で:dmesgは出力を書いて行われる前に、何らかの理由でgrepプロセスが終了した場合

`dmesg | grep hda` 

dmesgはSIGPIPEを受け取り、自分自身を終了します。これは、UNIX/Linuxプロセス(http://en.wikipedia.org/wiki/Unix_signal)の予想される動作です。 Pythonスクリプト自体(1 - p1が出力を生成して行われるp2終了する前にあれば、パイプを見ているプロセスがまだ実際に存在するため、これとは対照的に

は、subprocessを使用してPython実装では、SIGPIPEは送信されません。 p1p2を作成しました)。さらに重要なことは、スクリプトはパイプを見ていますが、内容を消費していないことです。パイプが無期限に開いていて、p1がリムボーに詰まっているということです。

明示的に閉じp1.stdout

がパイプからPythonスクリプトを切り離し、それは、そのような p2以外のプロセスがパイプを見ていないことになり - そのように p2が終了 p1前に、 p1が適切に人為的に何もせずに自分自身を終了する信号を取得しない場合パイプを開いたままにします。ここ

は、代替的な表現の説明である:docsから

http://www.enricozini.org/2009/debian/python-pipes/
+0

"アクティブなプロセスが見つからないパイプ"と"まだパイプを見ているプロセス "は本当に正確な言葉ではありません。 –

0

The p1.stdout.close() call after starting the p2 is important in order for p1 to receive a SIGPIPE if p2 exits before p1. 

それに接続されたプロセスなしでパイプに書き込みしようとしたときにSIGPIPEシグナルがプロセスに送られます。違った終わり方。 stdin=p1.stdoutを使用してp2を作成すると、パイプp1.stdoutに接続されている2つのプロセスがあります。親のPythonプロセスとp2です。 p2が時期尚早に終了しても、親プロセスがまだ実行されているため、SIGPIPEシグナルは送信されません。 p1.stdout.close()は、親/呼び出し側プロセスのp1.stdoutを終了し、そのファイル記述子を開いた唯一のプロセスとしてdmesgを残します。言い換えれば

、その後、何p1.stdout.close()がない場合:

  • p1.stdoutは、親プロセスで開いたままになります。 p2が終了すると(つまり、p1.stdoutを読む人がいないので)、p1は誰もが p1.stdoutを読み取っていないことを認識せず、 対応するOSパイプバッファがいっぱいになるまで、p1.stdoutに書き込み続けます。
  • p2が時期尚早に終了する場合、p1.stdoutはまだ親プロセスで開かれた であるため、SIGPIPEは生成されません。
2

Aは、うまくいけば、より体系的な説明:

  • パイプは、オペレーティングシステムによって管理されるインスタンスです。これは、単一の読取り終了と単一の書込み終了を有する。
  • 両端を複数のプロセスで開くことができます。しかし、パイプはまだ1つしかありません。つまり、複数のプロセスが同じパイプを共有することができます。
  • 端の1つを開いたプロセスは、対応するファイルハンドルを保持します。プロセスは活発にclose()それを再度行うことができます!プロセスが終了すると、オペレーティングシステムは対応するファイルハンドルを閉じます。
  • 関連するすべてのプロセスは、パイプの読み込み側を表すファイルハンドルをclose()にできます。それは間違っていませんが、これは完璧な状況です。
  • ここで、プロセスがパイプの書き込み側にデータを書き込み、読み取り側がもうオープンしない場合(読み取り側のファイルハンドルが開いていないプロセス)、POSIX準拠のオペレーティングシステムはSIGPIPE書き込みのプロセスはを知っていますもう読者がいません。

これは、受信プログラムがが暗黙的は、それが読み取りを停止した送信プログラムを伝えることが可能な標準的なメカニズムです。

cat bigfile | head -n5 

実際にはbigfile全体を読み取るかどうかは分かりましたか?いいえ、それはありません。catheadが終了するとすぐに(標準入力から5行を読み込んだ後に)SIGPIPE信号を取得します。重要なことは、catSIGPIPEに対応するように設計されています(これは重要な技術上の決定です)):ファイルの読み込みを停止して終了します。他のプログラムは、SIGPIPEを無視するように設計されています(これらは、この状況を自分で処理します - これはネットワークアプリケーションでは一般的です)。

制御プロセスでパイプの読み取り側を開いたままにしておくと、説明されているメカニズムが無効になります。 dmesggrepが終了したことを認識できません。

しかし、あなたの例は実際には良い例ではありません。 grep hdaはの入力全体をと読みます。 dmesgは、最初に終了するプロセスです。

関連する問題