web-dev-qa-db-ja.com

Unixプロセスが壊れたパイプで死ぬ原因は何ですか?

ここに私が考えたいくつかのオプションがありますが、どちらが正しいのかわかりません。

  1. パイプからの読み取り中にI/Oエラーが発生しました。
  2. パイプのもう一方の端に書き込むプロセスが失敗しました。
  3. パイプに書き込むことができるすべてのプロセスがそれを閉じました。
  4. パイプの書き込みバッファがいっぱいです。
  5. ピアが二重パイプのもう一方の方向を閉じました。
  6. パイプから読み取ることができるプロセスがないため、書き込みに失敗しました。
  7. システムコールがEPIPEエラーを返し、エラーハンドラがインストールされていませんでした。
31
siamii

プロセスは、(名前の有無にかかわらず)パイプまたはリーダーが残っていないタイプSOCK_STREAMのソケットに書き込もうとすると、SIGPIPEを受け取ります。

それは一般的に行動を望んでいます。典型的な例は次のとおりです。

_find . | head -n 1
_

findが終了した後、headが実行し続けることを望まない(そして、そのパイプで読み取るために開いている唯一のファイル記述子を閉じた)。

yesコマンドは通常、その信号に依存して終了します。

_yes | some-command
_

一部のコマンドが終了するまで「y」を書き込みます。

これは、コマンドが終了するときだけでなく、すべてのリーダーがパイプへの読み取りfdを閉じたときでもあることに注意してください。に:

_yes | ( sleep 1; exec <&-; ps -fC yes)
      1 2       1        0
_

1(サブシェル)、次に2(サブシェル+スリープ)、次に1(サブシェル)、その後0で、サブシェルが明示的にstdinを閉じた後にパイプからfdを読み取ります。そのとき、yesがSIGPIPEを受け取ります。 。

上記では、ほとんどのシェルはpipe(2)を使用していますが、_ksh93_はsocketpair(2)を使用していますが、動作は同じです。

プロセスがSIGPIPEを無視すると、書き込みシステムコール(通常はwriteですが、pwritesendsplice...の場合もあります)は、 EPIPEエラー。そのため、壊れたパイプを手動で処理するプロセスは、通常、SIGPIPEを無視し、EPIPEエラー時にアクションを実行します。

40

(6)

パイプから読み取ることができるプロセスがないため、書き込みに失敗しました。

記述子とフォークを複製しない限り、最初に開始できるプロセスは1つだけです。通常、パイプには1つのリーダーと1つのライターがあり、そのうちの1つが接続を閉じると、パイプは無効になります。名前付きパイプを使用している場合、複数の接続を(シリアルで)作成できますが、それぞれがこの意味で新しいパイプを表します。したがって、スレッドまたはプロセスへの「パイプ」は、ファイル記述子と同義です。

man 7 pipeから:

パイプの読み取り側を参照するすべてのファイル記述子が閉じられている場合、write(2)により、呼び出しプロセスに対してSIGPIPEシグナルが生成されます。呼び出しプロセスがこのシグナルを無視している場合、write(2)はエラーEPIPEで失敗します。

したがって、「壊れたパイプ」は、ライターにとってEOFがリーダーにとって何であるかです。

14
goldilocks

書き込みプロセスの前に読み取りプロセスが終了すると、パイプが壊れます。だから私は(6)で行きます

0
SidJ