Почему сигнал sigpipe, существует?
насколько я понимаю, SIGPIPE может произойти только в результате write(), который может (и делает) возвращают -1 и устанавливают errno до 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 заблокировано чем-то другим?). Заметьте, кстати, что это также позволяет нам более простую и чистую программу. Если бы вы зависели от кода ошибки от записи, вам нужно было бы что-то вроде: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.
теперь, убить больше и Б должно сломать в вашем сигнале обработчик. изучите стопку. Вы найдете, что читать все еще находится на рассмотрении. пусть обработчик сигнала продолжается и возвращается, и посмотрите на результат, возвращенный написать -- что будет затем быть -1.
[1] Естественно, вы напишете свою программу B В C. : -)
https://www.gnu.org/software/libc/manual/html_mono/libc.html
по этой ссылке написано:
труба или FIFO должны быть открыты на обоих концах одновременно. Если Вы читаете из файла pipe или FIFO, в котором нет никаких процессов, записывающих в него (возможно, потому, что все они закрыли файл или вышли), чтение возвращает конец файла. запись в канал или FIFO, который не имеет процесса чтения, рассматривается как условие ошибки; он генерирует SIGPIPE сигнал, и не удается с кодом ошибки EPIPE, если сигнал обрабатывается или блокируется.
- макрос: int SIGPIPE
сломанной трубы. Если вы используете pipes или FIFOs, вы должны спроектировать свое приложение так, чтобы один процесс открывал канал для чтения, прежде чем другой начнет писать. Если процесс чтения никогда не запускается или неожиданно завершается,запись в трубу или FIFO вызывает сигнал SIGPIPE. если SIGPIPE заблокирован, обработан или проигнорирован, вызов нарушителя завершается неудачно вместо ЭПИПЭ.
трубы и специальные файлы FIFO более подробно обсуждаются в Pipes и FIFOs.
Я думаю, что это, чтобы получить обработку ошибок правильно, не требуя много кода во всем записи в трубу.
некоторые программы игнорируют возвращаемое значение
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не генерируется сразу после завершения процесса чтения, но после попытки записать еще несколько данных в закрытый канал?
Comments