Control-c from the terminal sends an interrupt signal to the process group associated with the terminal, which includes the sleep process. Sending SIGINT via kill only reaches one process, and in this case it happens to be the wrong process because $$ returns the process ID of the shell, not the sleep process. Shells normally ignore SIGINT.
When true
exits, the read side of the pipe is closed, but yes
continues trying to write to the write side. This condition is called a "broken pipe", and it causes the kernel to send a SIGPIPE
signal to yes
. Since yes
does nothing special about this signal, it will be killed. If it ignored the signal, its write
call would fail with error code EPIPE
. Programs that do that have to be prepared to notice EPIPE
and stop writing, or they will go into an infinite loop.
If you do strace yes | true
1 you can see the kernel preparing for both possibilities:
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++
strace
is watching events via the debugger API, which first tells it about the system call returning with an error, and then about the signal. From yes
's perspective, though, the signal happens first. (Technically, the signal is delivered after the kernel returns control to user space, but before any more machine instructions are executed, so the write
"wrapper" function in the C library does not get a chance to set errno
and return to the application.)
1 Sadly, strace
is Linux-specific. Most modern Unixes have some command that does something similar, but it often has a different name, it probably doesn't decode syscall arguments as thoroughly, and sometimes it only works for root.
Best Answer
Signals are blocked for suspended processes. In a terminal:
In a second terminal:
In the first terminal:
However
SIGKILL
can't be blocked. Doing the same thing withkillall -9 yes
from the second terminal immediately gives this in theyes
terminal:Consequently if
kill -9 %1
doesn't terminate the process right away then eitherbash
isn't actually sending the signal until youfg
the process, or you have uncovered a bug in kernel.