Я не куплюсь на ранее принятый ответ. SIGPIPE
генерируется именно тогда, когда происходит write
сбой EPIPE
, а не заранее - на самом деле, один безопасный способ избежать SIGPIPE
без изменения расположения глобальных сигналов - это временно замаскировать его pthread_sigmask
, выполнить write
, а затем выполнить sigtimedwait
(с нулевым таймаутом), чтобы поглотить любой ожидающий SIGPIPE
сигнал (который отправляется в вызывающий поток, а не процесс), прежде чем снова его демаскировать.
Я считаю, что причина SIGPIPE
существует гораздо проще: установить разумное поведение по умолчанию для чистых «фильтрующих» программ, которые постоянно читают ввод, каким-то образом его преобразуют и записывают вывод. Без этого SIGPIPE
, если эти программы явно не обрабатывают ошибки записи и не завершают работу немедленно (что в любом случае может быть нежелательным для всех ошибок записи), они будут продолжать работу до тех пор, пока не закончатся входные данные, даже если их выходной канал был закрыт. Конечно, вы можете дублировать поведение SIGPIPE
, явно проверяя наличие EPIPE
и завершая работу, но вся цель SIGPIPE
заключалась в том, чтобы добиться этого поведения по умолчанию, когда программист ленив.
write
.Поскольку ваша программа может ожидать ввода-вывода или иным образом приостановлена. SIGPIPE асинхронно прерывает вашу программу, прерывая системный вызов, и поэтому может быть обработан немедленно.
Обновить
Рассмотрим трубопровод
A | B | C
.Для определенности предположим, что B - это канонический цикл копирования:
B
блокируется при вызове read (2), ожидающем данных сA
моментаC
завершения. Если вы дождетесь кода возврата от write (2) , когда B его увидит? Ответ, конечно, не будет до тех пор, пока A не запишет больше данных (что может занять долгое время - что, если A заблокирован чем-то другим?). Заметьте, кстати, что это также позволяет нам более простую и чистую программу. Если вы зависели от кода ошибки при записи, вам понадобится что-то вроде:Еще одно обновление
Ага, вы запутались в поведении пишите. Видите ли, когда дескриптор файла с ожидающей записью закрывается, сразу же происходит SIGPIPE. Пока запись вернет -1 конечном итоге , весь смысл сигнала состоит в том, чтобы асинхронно уведомить вас о том, что запись больше невозможна. Это часть того, что заставляет всю элегантную структуру совместных подпрограмм работать в UNIX.
Теперь я мог бы указать вам на целое обсуждение в любой из нескольких книг по системному программированию UNIX, но есть лучший ответ: вы можете проверить это сами. Напишите простую
B
программу [1] - у вас уже есть смелость, все, что вам нужно, этоmain
и некоторые включает - и добавьте обработчик сигнала дляSIGPIPE
. Запустите конвейер кака в другом окне терминала подключите отладчик к B и поместите точку останова в обработчик сигнала B.
Теперь убейте больше, и B должен сломать ваш обработчик сигналов. изучить стек. Вы обнаружите, что чтение еще не завершено. позвольте обработчику сигнала продолжить и вернуться, и посмотрите на результат, возвращаемый записью, который тогда будет -1.
[1] Естественно, вы будете писать свою программу B на C. :-)
источник
SIGPIPE
это не поставляется во время чтения, но во времяwrite
. Вам не нужно написать программу C , чтобы проверить это, просто запуститьcat | head
, иpkill head
в отдельном терминале. Вы увидите, что онcat
счастливо живет, ожидая в своемread()
- только когда вы вводите что-то и нажимаете Enter,cat
умирает со сломанным каналом именно потому, что он пытался записать вывод.SIGPIPE
не может быть доставлен,B
покаB
заблокирован,read
потому чтоSIGPIPE
не создается до тех пор, пока неB
попытаетсяwrite
. Ни один поток не может «ждать ввода-вывода или иным образом приостановлен» при одновременном вызовеwrite
.SIGPIPE
что вас поднимают при блокировкеread
? Я вообще не могу воспроизвести это поведение (и вообще не уверен, почему я принял это в первую очередь)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.
источник
Я думаю, что это нужно для правильной обработки ошибок, не требуя большого количества кода при записи в канал.
Некоторые программы игнорируют возвращаемое значение
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)
Я написал этот код ниже:
Выход:
Вы можете видеть, что в каждом случае
SIGPIPE
он получен только после того, как в процессе записи было записано более 3 символов (попытка записать).Разве это не доказывает, что
SIGPIPE
генерируется не сразу после завершения процесса чтения, а после попытки записи дополнительных данных в закрытый канал?источник