回答:
以前に承認された回答は購入しません。SIGPIPE
は事前にwrite
でEPIPE
はなく、で失敗したときに正確に生成されます-実際、SIGPIPE
グローバル信号の処理を変更せずに回避する1つの安全な方法は、で一時的にマスクしpthread_sigmask
、を実行してwrite
から、sigtimedwait
(タイムアウトなしで)実行して保留中のSIGPIPE
信号(に送信される)を消費することですプロセスではなく呼び出しスレッド)を再度マスク解除します。
理由SIGPIPE
はもっと単純だと思います。連続的に入力を読み取り、なんらかの方法で変換し、出力を書き込む純粋な「フィルター」プログラムの正しいデフォルトの動作を確立します。なしではSIGPIPE
、これらのプログラムが明示的に書き込みエラーを処理してすぐに終了しない限り(いずれにしても、すべての書き込みエラーにとって望ましい動作とは限りません)、出力パイプが閉じていても、入力がなくなるまで実行を続けます。もちろんSIGPIPE
、明示的にチェックしEPIPE
て終了することでの動作を複製できますが、その目的はSIGPIPE
、プログラマが怠惰なときにデフォルトでこの動作を実現することでした。
あなたのプログラムがI / Oを待っているか、そうでなければ一時停止している可能性があるからです。SIGPIPEはプログラムに非同期に割り込み、システムコールを終了するため、すぐに処理できます。
更新
パイプラインについて考えてみましょうA | B | C
。
明確にするために、Bは正規のコピーループであると仮定します。
while((sz = read(STDIN,bufr,BUFSIZE))>=0)
write(STDOUT,bufr,sz);
B
が終了するときからのデータを待つread(2)呼び出しでブロックされます。write(2)からの戻りコードを待つ場合、Bはいつそれを見るでしょうか?もちろん、答えはAがさらにデータを書き込むまではありません(これは長い待ち時間になる可能性があります-Aが他の何かによってブロックされている場合はどうなりますか?)。ちなみに、これにより、よりシンプルでクリーンなプログラムも作成できることに注意してください。書き込みのエラーコードに依存している場合は、次のようなものが必要になります。A
C
while((sz = read(STDIN,bufr,BUFSIZE))>=0)
if(write(STDOUT,bufr,sz)<0)
break;
別のアップデート
ああ、あなたは書き込みの動作について混乱しています。保留中の書き込みを含むファイル記述子が閉じられると、SIGPIPEがすぐに発生します。書き込みは最終的に-1を返しますが、シグナルの要点は、書き込みができなくなったことを非同期で通知することです。これは、パイプのエレガントなコルーチン構造全体がUNIXで機能するための一部です。
さて、いくつかのUNIXシステムプログラミングブックでの全体的な議論を紹介することができますが、もっと良い答えがあります。これを自分で確認できます。簡単なB
プログラム[1] を書いてください-すでに根性があり、必要なのはmain
といくつかのインクルードだけです-そしてのためのシグナルハンドラを追加しSIGPIPE
ます。次のようなパイプラインを実行する
cat | B | more
別のターミナルウィンドウで、デバッガーをBに接続し、Bシグナルハンドラー内にブレークポイントを配置します。
今、もっと殺すとBはあなたのシグナルハンドラーで壊れるはずです。スタックを調べます。読み取りがまだ保留中であることがわかります。シグナルハンドラが進みましょうと戻り、およびによって返された結果を見て書き込み -であろう、その後も-1。
[1]もちろん、BプログラムはCで記述します。:-)
SIGPIPE
は読み取り中には配信されませんが、中には配信されwrite
ます。あなただけ実行し、それをテストするためのCプログラムを記述する必要はありませんcat | head
し、pkill head
別の端末インチ あなたはそれを見ることができますcat
喜んで待っているに住んでそのread()
-onlyあなたが何かを入力し、プレスがする入力したときにcat
、それは書き込み出力しようとしたまさにので、壊れたパイプで金型を。
SIGPIPE
がを試行するまで生成されないため、ブロックされているB
間B
は-1 を配信できません。同時に呼び出しているスレッドは、「I / Oを待機している、または中断されている」ことはありません。read
SIGPIPE
B
write
write
SIGPIPE
ブロックされている間に発生したことを示す完全なプログラムを投稿できますread
か?私はその振る舞いをまったく再現することができません(そして、なぜ私がこれを最初に受け入れたのか実際にはわかりません)
https://www.gnu.org/software/libc/manual/html_mono/libc.html
このリンクは言う:
パイプまたはFIFOは、両端で同時に開いている必要があります。プロセスへの書き込みがないパイプまたはFIFOファイルから読み取る場合(おそらく、それらがすべてファイルを閉じたか、終了したため)、読み取りはファイルの終わりを返します。読み取りプロセスのないパイプまたはFIFOへの書き込みは、エラー状態として扱われます。SIGPIPEシグナルを生成し、シグナルが処理またはブロックされると、エラーコードEPIPEで失敗します。
—マクロ:int SIGPIPE
壊れたパイプ。パイプまたはFIFOを使用する場合、別のプロセスが書き込みを開始する前に、あるプロセスが読み取り用にパイプを開くようにアプリケーションを設計する必要があります。読み取りプロセスが開始されない、または予期せず終了する場合、パイプまたはFIFOへの書き込みによりSIGPIPEシグナルが発生します。SIGPIPEがブロック、処理、または無視される場合、問題のある呼び出しはEPIPEで失敗します。
パイプとFIFO特殊ファイルについては、パイプとFIFOで詳しく説明しています。
マシン情報:
Linux 3.2.0-53-generic#81-Ubuntu SMP Thu Aug 22 21:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux
gccバージョン4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)
私はこのコードを以下に書きました:
// Writes characters to stdout in an infinite loop, also counts
// the number of characters generated and prints them in sighandler
// writestdout.c
# include <unistd.h>
# include <stdio.h>
# include <signal.h>
# include <string.h>
int writeCount = 0;
void sighandler(int sig) {
char buf1[30] ;
sprintf(buf1,"signal %d writeCount %d\n", sig, writeCount);
ssize_t leng = strlen(buf1);
write(2, buf1, leng);
_exit(1);
}
int main() {
int i = 0;
char buf[2] = "a";
struct sigaction ss;
ss.sa_handler = sighandler;
sigaction(13, &ss, NULL);
while(1) {
/* if (writeCount == 4) {
write(2, "4th char\n", 10);
} */
ssize_t c = write(1, buf, 1);
writeCount++;
}
}
// Reads only 3 characters from stdin and exits
// readstdin.c
# include <unistd.h>
# include <stdio.h>
int main() {
ssize_t n ;
char a[5];
n = read(0, a, 3);
printf("read %zd bytes\n", n);
return(0);
}
出力:
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11486
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 429
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 281
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 490
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 433
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 318
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 468
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11866
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 496
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 284
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 271
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 416
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11268
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 427
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 8812
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 394
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 10937
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 10931
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 3554
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 499
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 283
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11133
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 451
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 493
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 233
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11397
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 492
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 547
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 441
すべてのインスタンスSIGPIPE
で、書き込みプロセスによって3文字以上が書き込まれる(試行される)後にのみ受信されることがわかります。
これはSIGPIPE
、読み取りプロセスが終了した直後ではなく、閉じたパイプにさらにデータを書き込もうとした後に生成されたことを証明していませんか?
write
。