Почему существует SIGPIPE?

насколько я понимаю, SIGPIPE может произойти только в результате write(), который может (и делает) возвращают -1 и устанавливают errno to EPIPE... Так почему у нас дополнительные накладные расходы на сигнал? Каждый раз, когда я работаю с трубами, я игнорирую SIGPIPE и никогда не чувствовал никакой боли в итоге, я что-то пропустила?

5 ответов


Я не покупаю ранее принятый ответ. SIGPIPE создается именно тогда, когда write выдает EPIPE, а не заранее - на самом деле один безопасный способ избежать SIGPIPE без изменения глобальных диспозиций сигнала временно маскировать его с pthread_sigmask выполнить write выполнить sigtimedwait (С нулевым таймаутом), чтобы потреблять любой ожидающий SIGPIPE сигнал (который отправляется вызывающему потоку, а не процессу), прежде чем снова разоблачить его.

Я верю в причину SIGPIPE существует гораздо проще: установление нормального поведения по умолчанию для чистых" фильтрующих " программ, которые непрерывно читают вход, каким-то образом преобразуют его и записывают выход. Без SIGPIPE, Если эти программы явно не обрабатывают ошибки записи и немедленно не выходят (что в любом случае не может быть желательным поведением для всех ошибок записи), они будут продолжать работать до тех пор, пока не закончатся входные данные, даже если их выходной канал был закрыт. Конечно, вы можете дублировать поведение SIGPIPE явно проверяя EPIPE и выход, но вся цель SIGPIPE должно было достичь этого поведения по умолчанию, когда программист ленив.


потому что ваша программа может ждать ввода-вывода или иным образом приостановлена. SIGPIPE прерывает вашу программу асинхронно, завершая системный вызов, и поэтому может быть обработан немедленно.

обновление

рассмотрим трубопровода A | B | C.

просто для определенности мы предположим, что B-канонический цикл копирования:

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

B блокируется на Читать(2) вызов в ожидании данных от A когда C завершается. Если вы ждете код возврата от написать(2), когда б его увидеть? Ответ, конечно, не до тех пор, пока A не записывает больше данных (что может быть долгим ожиданием-что, если A заблокирован чем-то другим?). Обратите внимание, кстати, что это также позволяет нам более простую и чистую программу. Если бы вы зависели от кода ошибки от write, вам нужно было бы что-то вроде:

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 должны сломать в вашем сигнале обработчик. проверьте стопку. Вы найдете, что читать все еще в ожидании. пусть обработчик сигнала продолжит и вернется, и посмотрите на результат, возвращенный написать -- что будет затем be -1.

[1] Естественно, вы напишете свою программу B В C.: -)


https://www.gnu.org/software/libc/manual/html_mono/libc.html

по этой ссылке написано:

труба или FIFO должны быть открыты на обоих концах одновременно. Если Вы читаете из файла канала или FIFO, в который не записываются процессы (возможно, потому, что все они закрыли файл или вышли), чтение возвращает конец файла. запись в канал или FIFO, который не имеет процесса чтения, рассматривается как условие ошибки; он генерирует SIGPIPE сигнал, и сбой с кодом ошибки EPIPE, если сигнал обрабатывается или блокируется.

- макрос: int SIGPIPE

сломанной трубы. Если вы используете каналы или FIFOs, вы должны сконструировать приложение так, чтобы один процесс открывал канал для чтения, прежде чем другой начнет писать. Если процесс чтения никогда не запускается или неожиданно завершается,запись в трубу или FIFO вызывает сигнал SIGPIPE. если SIGPIPE заблокирован, обработан или проигнорирован, вызов-нарушитель завершается неудачно вместо ЭПИПЭ.

трубы и специальные файлы FIFO более подробно обсуждаются в трубах и FIFOs.


Я думаю, что это, чтобы получить правильную обработку ошибок, не требуя много кода во всем, что пишется в трубу.

некоторые программы игнорируют возвращаемое значение write(), без SIGPIPE они будут бесполезно генерировать все выходные данные.

программы, которые проверяют возвращаемое значение write() вероятно, напечатать сообщение об ошибке, если он терпит неудачу; это неуместно для сломанной трубы, как это на самом деле не ошибка для всего трубопровода.


информация о машине:

Linux 3.2.0-53-generic #81-Ubuntu SMP Чт Авг 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 не генерируется сразу после завершения процесса чтения, но после попытки записать еще несколько данных в закрытый канал?