私の理解では、SIGPIPE
はwrite()
の結果としてのみ発生します。これは-1を返すことができ(実際に)、errno
をEPIPE
に設定します...では、なぜ信号の余分なオーバーヘッドがあるのですか?パイプを使用するたびにSIGPIPE
を無視し、結果として痛みを感じたことはありませんが、何かが足りませんか?
以前に受け入れられた答えを購入しません。 SIGPIPE
は、事前にではなく、write
がEPIPE
で失敗したときに正確に生成されます。実際、SIGPIPE
を回避する安全な方法の1つは、一時的にpthread_sigmask
でマスクし、write
を実行してから、sigtimedwait
(タイムアウトなし)を実行して、保留中のSIGPIPE
信号(呼び出しスレッドに送信)を消費します。 、プロセスではなく)、再度マスクを解除する前に。
SIGPIPE
が存在する理由ははるかに単純だと思います。入力を連続的に読み取り、何らかの形で変換し、出力を書き込む純粋な「フィルター」プログラムの正常なデフォルト動作を確立します。 SIGPIPE
がない場合、これらのプログラムが明示的に書き込みエラーを処理してすぐに終了しない限り(これはすべての書き込みエラーに望ましい動作ではないかもしれません)、出力パイプが停止しても入力がなくなるまで実行を続けます閉じられました。 SIGPIPE
を明示的にチェックして終了することでEPIPE
の動作を複製できますが、SIGPIPE
の目的はプログラマが怠zyな場合にデフォルトでこの動作を実現することでした。
プログラムがI/Oを待機している、または一時停止している可能性があるため。 SIGPIPEはプログラムを非同期的に中断し、システムコールを終了するため、すぐに処理できます。
更新
パイプラインA | B | C
を検討してください。
明確にするために、Bが正規のコピーループであると仮定します。
while((sz = read(STDIN,bufr,BUFSIZE))>=0)
write(STDOUT,bufr,sz);
B
が終了すると、A
からのデータを待機するread(2)呼び出しでC
がブロックされます。 write(2)からの戻りコードを待つ場合、Bはそれをいつ見るでしょうか?もちろん、答えは、Aがさらにデータを書き込むまでではありません(長い待ち時間になる可能性があります。Aが他の何かによってブロックされた場合はどうなりますか?)。ところで、これにより、よりシンプルでクリーンなプログラムが可能になることに注意してください。書き込みのエラーコードに依存している場合は、次のようなものが必要です。
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シグナルハンドラー内にブレークポイントを配置します。
ここで、moreを強制終了すると、Bがシグナルハンドラーで中断します。スタックを調べます。 readはまだ保留中であることがわかります。シグナルハンドラを続行して戻り、writeによって返された結果を見てみましょう。これはthen-1になります。
[1]当然、BプログラムはCで作成します。
https://www.gnu.org/software/libc/manual/html_mono/libc.html
このリンクは言う:
パイプまたはFIFOは両端で同時に開く必要があります。パイプまたはFIFO書き込みプロセスがないファイルから読み取る場合(おそらく、ファイルをすべて閉じた、または終了したため)、読み取りはファイルの終わりを返します。パイプへの書き込みまたはFIFO that '読み取りプロセスがエラー状態として扱われる場合、SIGPIPEシグナルが生成され、シグナルが処理またはブロックされるとエラーコードEPIPEで失敗します。
—マクロ:int SIGPIPE
壊れたパイプ。パイプまたはFIFOを使用する場合、あるプロセスが読み取り用にパイプを開き、別のプロセスが書き込みを開始するようにアプリケーションを設計する必要があります。読み取りプロセスが開始されない、または予期せず終了しない場合、パイプへの書き込みまたはFIFO SIGPIPEシグナルが発生します。 SIGPIPEはブロック、処理、または無視され、問題のある呼び出しは代わりにEPIPEで失敗します。
パイプとFIFO特殊ファイルについては、パイプとFIFOで詳しく説明します。
パイプに書き込むすべてに多くのコードを必要とせずにエラー処理を正しくすることだと思います。
一部のプログラムは、write()
の戻り値を無視します。 SIGPIPE
がないと、すべての出力を無駄に生成します。
write()
の戻り値をチェックするプログラムは、失敗するとエラーメッセージを出力する可能性があります。これは、パイプライン全体のエラーではないため、破損したパイプには不適切です。
マシン情報:
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
が生成されないことを証明しませんか?