web-dev-qa-db-ja.com

なぜSIGPIPEが存在するのですか?

私の理解では、SIGPIPEwrite()の結果としてのみ発生します。これは-1を返すことができ(実際に)、errnoEPIPEに設定します...では、なぜ信号の余分なオーバーヘッドがあるのですか?パイプを使用するたびにSIGPIPEを無視し、結果として痛みを感じたことはありませんが、何かが足りませんか?

88
Shea Levy

以前に受け入れられた答えを購入しません。 SIGPIPEは、事前にではなく、writeEPIPEで失敗したときに正確に生成されます。実際、SIGPIPEを回避する安全な方法の1つは、一時的にpthread_sigmaskでマスクし、writeを実行してから、sigtimedwait(タイムアウトなし)を実行して、保留中のSIGPIPE信号(呼び出しスレッドに送信)を消費します。 、プロセスではなく)、再度マスクを解除する前に。

SIGPIPEが存在する理由ははるかに単純だと思います。入力を連続的に読み取り、何らかの形で変換し、出力を書き込む純粋な「フィルター」プログラムの正常なデフォルト動作を確立します。 SIGPIPEがない場合、これらのプログラムが明示的に書き込みエラーを処理してすぐに終了しない限り(これはすべての書き込みエラーに望ましい動作ではないかもしれません)、出力パイプが停止しても入力がなくなるまで実行を続けます閉じられました。 SIGPIPEを明示的にチェックして終了することでEPIPEの動作を複製できますが、SIGPIPEの目的はプログラマが怠zyな場合にデフォルトでこの動作を実現することでした。

106
R..

プログラムが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で作成します。

24
Charlie Martin

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で詳しく説明します。

7
abc

パイプに書き込むすべてに多くのコードを必要とせずにエラー処理を正しくすることだと思います。

一部のプログラムは、write()の戻り値を無視します。 SIGPIPEがないと、すべての出力を無駄に生成します。

write()の戻り値をチェックするプログラムは、失敗するとエラーメッセージを出力する可能性があります。これは、パイプライン全体のエラーではないため、破損したパイプには不適切です。

5
jilles

マシン情報:

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が生成されないことを証明しませんか?

2
abc