Nie kupuję wcześniej zaakceptowanej odpowiedzi. SIGPIPE
jest generowany dokładnie wtedy, gdy wystąpi write
awaria EPIPE
, a nie wcześniej - w rzeczywistości jednym bezpiecznym sposobem uniknięcia SIGPIPE
bez zmiany globalnych dyspozycji sygnału jest tymczasowe zamaskowanie go pthread_sigmask
, wykonanie write
, a następnie wykonanie sigtimedwait
(z zerowym limitem czasu), aby skonsumować każdy oczekujący SIGPIPE
sygnał (który jest wysyłany do wątek wywołujący, a nie proces) przed ponownym zdemaskowaniem.
Uważam, że przyczyna SIGPIPE
jest znacznie prostsza: ustanowienie rozsądnego zachowania domyślnego dla czystych programów „filtrujących”, które stale odczytują dane wejściowe, w jakiś sposób je przekształcają i zapisują dane wyjściowe. Bez SIGPIPE
tych programów, chyba że te programy jawnie obsługują błędy zapisu i natychmiast kończą pracę (co i tak może nie być pożądanym zachowaniem dla wszystkich błędów zapisu), będą działać do momentu wyczerpania danych wejściowych, nawet jeśli ich potok wyjściowy został zamknięty. Oczywiście możesz zduplikować zachowanie programu SIGPIPE
, jawnie sprawdzając EPIPE
i kończąc, ale głównym celem SIGPIPE
było osiągnięcie tego zachowania domyślnie, gdy programista jest leniwy.
write
.Ponieważ Twój program może czekać na we / wy lub w inny sposób zawieszony. SIGPIPE przerywa program asynchronicznie, kończąc wywołanie systemowe, dzięki czemu może być obsłużony natychmiast.
Aktualizacja
Rozważ rurociąg
A | B | C
.Dla jasności przyjmiemy, że B jest kanoniczną pętlą kopiowania:
B
jest blokowany w wywołaniu read (2) oczekującym na dane odA
momentuC
zakończenia. Jeśli czekasz na kod powrotu z write (2) , kiedy B go zobaczy? Odpowiedź oczywiście nie brzmi, dopóki A nie zapisze większej ilości danych (co może oznaczać długie oczekiwanie - a co, jeśli A zostanie zablokowane przez coś innego?). Zauważ przy okazji, że to również pozwala nam na prostszy, czystszy program. Jeśli polegałeś na kodzie błędu z zapisu, potrzebujesz czegoś takiego:Kolejna aktualizacja
Aha, jesteś zdezorientowany co do zachowania pisma. Widzisz, kiedy deskryptor pliku z oczekującym zapisem jest zamknięty, SIGPIPE dzieje się natychmiast. Podczas gdy zapis w końcu zwróci -1 , celem sygnału jest asynchroniczne powiadomienie, że zapis nie jest już możliwy. Jest to część tego, co sprawia, że cała elegancka współrutyna struktura potoków działa w systemie UNIX.
Mógłbym teraz wskazać Ci całą dyskusję w dowolnej z kilku książek o programowaniu w systemach UNIX, ale jest lepsza odpowiedź: możesz to sprawdzić samodzielnie. Napisz prosty
B
program [1] - masz już odwagę, wszystko czego potrzebujesz to a,main
a niektóre zawierają - i dodaj obsługę sygnału dlaSIGPIPE
. Uruchom potok, taki jakaw innym oknie terminala dołącz debugger do B i umieść punkt przerwania wewnątrz procedury obsługi sygnału B.
Teraz zabij więcej, a B powinien włamać się do twojego modułu obsługi sygnału. zbadaj stos. Przekonasz się, że odczyt jest nadal w toku. niech obsługa sygnału kontynuuje i wraca, i spójrz na wynik zwrócony przez write - który będzie wtedy wynosił -1.
[1] Oczywiście napiszesz swój program B w C. :-)
źródło
SIGPIPE
nie jest dostarczany podczas odczytu, ale podczaswrite
. Nie trzeba napisać program w C, aby go przetestować, wystarczy uruchomićcat | head
, apkill head
w oddzielnym terminalu. Zobaczysz, żecat
szczęśliwie żyje czekając w swoim -read()
tylko wtedy, gdy coś wpiszesz i naciśniesz enter,cat
umiera z pękniętą potoką, dokładnie dlatego, że próbował napisać wyjście.SIGPIPE
nie można dostarczyć doB
podczas gdyB
jest zablokowany,read
ponieważSIGPIPE
nie jest generowany, dopóki nieB
spróbujewrite
. Żaden wątek nie może „czekać na I / O lub w inny sposób zawieszony” podczas jednoczesnego wywoływaniawrite
.SIGPIPE
został zgłoszony, gdy jest zablokowany naread
? W ogóle nie mogę odtworzyć tego zachowania (i właściwie nie jestem pewien, dlaczego zaakceptowałem to w pierwszej kolejności)https://www.gnu.org/software/libc/manual/html_mono/libc.html
Ten link mówi:
Rura lub FIFO muszą być otwarte na obu końcach jednocześnie. Jeśli czytasz z potoku lub pliku FIFO, który nie ma żadnych procesów zapisujących do niego (być może dlatego, że wszystkie zamknęły plik lub zakończyły pracę), odczyt zwraca koniec pliku. Zapis do potoku lub FIFO, który nie ma procesu odczytu, jest traktowany jako warunek błędu; generuje sygnał SIGPIPE i kończy się niepowodzeniem z kodem błędu EPIPE, jeśli sygnał jest obsługiwany lub blokowany.
- Makro: int SIGPIPE
Złamana rura. Jeśli używasz potoków lub FIFO, musisz zaprojektować aplikację tak, aby jeden proces otwierał potok do odczytu, zanim inny zacznie pisać. Jeśli proces odczytu nigdy się nie rozpoczyna lub kończy się nieoczekiwanie, zapis do potoku lub FIFO wywołuje sygnał SIGPIPE. Jeśli SIGPIPE jest blokowany, obsługiwany lub ignorowany, wywołanie powodujące naruszenie kończy się niepowodzeniem z EPIPE.
Potoki i pliki specjalne FIFO są omówione bardziej szczegółowo w potokach i FIFO.
źródło
Myślę, że jest to poprawna obsługa błędów bez konieczności pisania dużej ilości kodu we wszystkim, co jest zapisywane w potoku.
Niektóre programy ignorują zwracaną wartość
write()
; bezSIGPIPE
nich generowałyby bezużytecznie cały wynik.Programy, które sprawdzają zwracaną wartość
write()
prawdopodobnie wyświetlają komunikat o błędzie, jeśli to się nie powiedzie; jest to niewłaściwe w przypadku pękniętej rury, ponieważ w rzeczywistości nie jest to błąd dla całego rurociągu.źródło
Informacje o maszynie:
Linux 3.2.0-53-generic # 81-Ubuntu SMP czw. 22 sierpnia 21:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux
gcc w wersji 4.6.3 (Ubuntu / Linaro 4.6.3-1ubuntu5)
Napisałem ten kod poniżej:
Wynik:
Możesz zobaczyć, że w każdym przypadku
SIGPIPE
jest odbierany dopiero po tym, jak więcej niż 3 znaki są (próbowano) napisać przez proces pisania.Czy to nie dowodzi, że
SIGPIPE
nie jest generowany natychmiast po zakończeniu odczytu, ale po próbie zapisania większej ilości danych do zamkniętego potoku?źródło