SIGPIPE가 존재하는 이유는 무엇입니까?


93

내 이해에 따르면 -1을 반환 하고 ...로 설정할 수있는 (그리고 수행하는) SIGPIPEa의 결과로만 발생할 수 있습니다. 왜 우리는 신호의 추가 오버 헤드를 가지고 있습니까? 파이프로 작업 할 때마다 무시 하고 그 결과로 어떤 고통도 느끼지 못했습니다. 뭔가 놓치고 있습니까?write()errnoEPIPESIGPIPE

답변:


112

나는 이전에 받아 들인 대답을 사지 않습니다. 사전이 아닌으로 실패 SIGPIPE할 때 정확히 생성됩니다. 실제로 전역 신호 처리를 변경 하지 않고 피할 수있는 안전한 방법 중 하나 는 일시적으로으로 마스킹하고을 수행 한 다음 (제로 타임 아웃으로) 수행 하여 보류중인 신호 를 소비 하는 것입니다. 다시 마스크를 해제하기 전에 프로세스가 아닌 호출 스레드).writeEPIPESIGPIPEpthread_sigmaskwritesigtimedwaitSIGPIPE

그 이유 SIGPIPE가 훨씬 더 간단 하다고 생각합니다 . 입력을 계속 읽고 어떻게 든 변환하고 출력을 쓰는 순수한 "필터"프로그램에 대한 정상적인 기본 동작을 설정하는 것입니다. 를 사용하지 SIGPIPE않으면 이러한 프로그램이 명시 적으로 쓰기 오류를 처리하고 즉시 종료하지 않는 한 (어쨌든 모든 쓰기 오류에 대해 원하는 동작이 아닐 수 있음) 출력 파이프가 닫혀 있어도 입력이 부족할 때까지 계속 실행됩니다. 물론 SIGPIPE명시 적으로 확인 EPIPE하고 종료 하여의 동작을 복제 할 수 있지만의 전체 목적은 SIGPIPE프로그래머가 게으른 경우 기본적으로이 동작을 달성하는 것입니다 .


15
+1. 단서는 SIGPIPE가 기본적으로 사용자를 죽인다는 사실입니다. 시스템 호출을 중단하도록 설계되지 않았으며 프로그램을 종료하도록 설계되었습니다! 신호 처리기에서 신호를 처리 할 수 ​​있다면의 반환 코드도 처리 할 수 ​​있습니다 write.
Nicholas Wilson

2
당신 말이 맞아요, 제가 왜 그것을 받아들 였는지 모르겠어요. 이 대답은 의미가 있지만 IMO에서 예를 들어 Linux에서 이러한 게으름이 libc가 아닌 커널에 의해 달성된다는 것은 이상합니다.
Shea Levy

5
이 대답은 기본적으로 "예외가 없었기 때문에"로 요약되는 것처럼 들립니다. 그러나 C에서 리턴 코드를 무시하는 사람들은 write () 호출보다 훨씬 더 광범위한 문제입니다. 쓰기를 특별하게 만드는 이유는 무엇입니까? 자체 신호가 필요합니까? 아마도 순수 필터 프로그램은 제가 상상하는 것보다 훨씬 더 일반적 일 것입니다.
Arvid

@Arvid SIGPIPE는 필터 프로그램이 매우 일반적인 환경에서 겪었던 문제를 해결하기 위해 Unix 사람들이 발명했습니다. 우리가해야 할 일은 시스템을 불러오는 부트 스크립트를 읽는 것뿐입니다.
Kaz

@SheaLevy 어떤 Unix 시스템이 순수하게 libc에서 SIGPIPE를 구현합니까?
Kaz

23

프로그램이 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가 다른 것에 의해 차단되면 어떻게 될까요?). 그건 그렇고, 이것은 또한 우리에게 더 간단하고 깨끗한 프로그램을 허용합니다. 쓰기 오류 코드에 의존했다면 다음과 같은 것이 필요합니다.AC

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    if(write(STDOUT,bufr,sz)<0)
        break;

또 다른 업데이트

아하, 당신은 쓰기 동작에 대해 혼란스러워합니다. 보류중인 쓰기가있는 파일 설명자가 닫히면 SIGPIPE가 바로 발생합니다. 쓰기는 결국 -1을 반환하지만 신호의 요점은 쓰기가 더 이상 가능하지 않다는 것을 비동기 적으로 알리는 것입니다. 이것은 파이프의 전체 우아한 코 루틴 구조가 UNIX에서 작동하도록 만드는 요소의 일부입니다.

이제 여러 UNIX 시스템 프로그래밍 책에서 전체 토론을 가리킬 수 있지만 더 나은 답이 있습니다. 직접 확인할 수 있습니다. 간단한 B프로그램을 작성하세요 [1]-이미 배짱이 있고, 필요한 것은 a main와 일부 포함입니다-그리고 SIGPIPE. 다음과 같은 파이프 라인 실행

cat | B | more

다른 터미널 창에서 디버거를 B에 연결하고 B 신호 처리기 내부에 중단 점을 넣습니다.

이제 더 많은 것을 죽이고 B는 신호 처리기에서 중단됩니다. 스택을 조사하십시오. 당신은 것을 찾을 수 읽기가 아직 보류 중입니다. 에 의해 반환 된 결과에서 진행 핸들러 신호와 반환, 및 살펴 보자 쓰기 - 할 다음 일 -1.

[1] 당연히 B 프로그램을 C로 작성합니다. :-)


3
B가 SIGPIPE로 C의 종료를 더 빨리 보게되는 이유는 무엇입니까? B는 STDIN에 무언가가 기록 될 때까지 읽기에서 차단 된 상태로 남아 있으며, 이때 write ()를 호출하고 SIGPIPE가 발생 / -1이 반환됩니다.
Shea Levy 2011

2
저는 그 대답이 정말 마음에 듭니다. SIGPIPE를 사용하면 파이프 라인의 출력 끝에서 즉시 죽음이 다시 전파됩니다. 이것이 없으면 파이프의 N 개 요소 각각에 대해 복사 프로그램의 최대 한 사이클이 파이프 라인을 죽이고 입력 측이 끝까지 도달하지 않는 N 개 라인을 생성하게합니다.
Yttrill 2012

18
이 대답은 틀 렸습니다. SIGPIPE되어 있지 하지만 중, 읽기 중 전달 write. 당신은 그것을 테스트하는 C 프로그램을 작성해야합니다, 단지 실행하지 않는 cat | head, 그리고 pkill head별도의 터미널이다. 당신은 cat기다릴 read()때 행복하게 살아가는 것을 볼 수있을 것입니다.- cat단지 당신이 무언가를 입력하고 엔터를 눌렀을 때만 출력을 쓰려고했기 때문에 깨진 파이프와 함께 죽습니다.
user4815162342

5
-1 SIGPIPE은을 ( BB) 시도 할 때까지 생성되지 read않기 때문에 차단 된 동안에 전달할 수 없습니다 . 동시에 호출하는 동안 스레드는 "I / O를 기다리거나 일시 중단"할 수 없습니다 . SIGPIPEBwritewrite
Dan Molding

3
SIGPIPE차단 된 동안 제기되는 것을 보여주는 전체 프로그램을 게시 할 수 있습니까 read? 나는 전혀 그 동작을 재현하지 (내가 처음에이 수락 이유를 실제로 확실하지 않다) 수
시어 레비에게

7

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에서 자세히 설명합니다.


5

나는 파이프에 쓰는 모든 것에 많은 코드를 요구하지 않고 올바른 오류 처리를 얻는 것이라고 생각합니다.

일부 프로그램은 반환 값을 무시합니다 write(). 없이 SIGPIPE그들은 쓸데없이 모든 출력을 생성합니다.

반환 값을 확인하는 프로그램은 write()실패 할 경우 오류 메시지를 인쇄합니다. 이것은 전체 파이프 라인에 대해 실제로 오류가 아니므로 깨진 파이프에는 부적절합니다.


2

기계 정보 :

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읽기 프로세스가 종료 된 직후에 생성되지 않고 닫힌 파이프에 더 많은 데이터를 쓰려고 시도한 후에 생성 된다는 것을 증명 하지 않습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.