From my understanding, SIGPIPE can only occur as the result of a write(), which can (and does) return -1 and set errno to EPIPE... So why do we have the extra overhead of a signal? Every time I work with pipes I ignore SIGPIPE and have never felt any pain as a result, am I missing something?
I don't buy the previously-accepted answer. SIGPIPE is generated exactly when the write fails with EPIPE, not beforehand - in fact one safe way to avoid SIGPIPE without changing global signal dispositions is to temporarily mask it with pthread_sigmask, perform the write, then perform sigtimedwait (with zero timeout) to consume any pending SIGPIPE signal (which is sent to the calling thread, not the process) before unmasking it again.
I believe the reason SIGPIPE exists is much simpler: establishing sane default behavior for pure "filter" programs that continuously read input, transform it somehow, and write output. Without SIGPIPE, unless these programs explicitly handle write errors and immediately exit (which might not be the desired behavior for all write errors, anyway), they will continue running until they run out of input even if their output pipe has been closed. Sure you can duplicate the behavior of SIGPIPE by explicitly checking for EPIPE and exiting, but the whole purpose of SIGPIPE was to achieve this behavior by default when the programmer is lazy.
Because your program may be waiting for I/O or otherwise suspended. A SIGPIPE interrupts your program asynchronously, terminating the system call, and so can be handled immediately.
Update
Consider a pipeline A | B | C.
Just for definiteness, we'll assume that B is the canonical copy loop:
while((sz = read(STDIN,bufr,BUFSIZE))>=0) write(STDOUT,bufr,sz); B is blocked on the read(2) call waiting for data from A when C terminates. If you wait for the return code from write(2), when will B see it? The answer, of course, is not until A writes more data (which could be a long wait -- what if A is blocked by something else?). Notice, by the way, that this also allows us a simpler, cleaner program. If you depended on the error code from write, you'd need something like:
while((sz = read(STDIN,bufr,BUFSIZE))>=0) if(write(STDOUT,bufr,sz)<0) break; Another update
Aha, you're confused about the behavior of the write. You see, when the file descriptor with the pending write is closed, the SIGPIPE happens right then. While the write will return -1 eventually, the whole point of the signal is to notify you asynchronously that the write is no longer possible. This is part of what makes the whole elegant co-routine structure of pipes work in UNIX.
Now, I could point you to a whole discussion in any of several UNIX system programming books, but there's a better answer: you can verify this yourself. Write a simple B program[1] -- you've got the guts already, all you need is a main and some includes -- and add a signal handler for SIGPIPE. Run a pipeline like
cat | B | more
and in another terminal window, attach a debugger to B and put a breakpoint inside the B signal handler.
Now, kill the more and B should break in your signal handler. examine the stack. You'll find that the read is still pending. let the signal handler proceed and return, and look at the result returned by write -- which will then be -1.
[1] Naturally, you'll write your B program in C. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With