The question contains its own answer. Sending the SIGINT
to the cat
process with kill
is a perfect simulation of what happens when you press ^C
.
To be more precise, the interrupt character (^C
by default) sends SIGINT
to every process in the terminal's foreground process group. If instead of cat
you were running a more complicated command involving multiple processes, you'd have to kill the process group to achieve the same effect as ^C
.
When you run any external command without the &
background operator, the shell creates a new process group for the command and notifies the terminal that this process group is now in the foreground. The shell is still in its own process group, which is no longer in the foreground. Then the shell waits for the command to exit.
That's where you seem to have become the victim by a common misconception: the idea that the shell is doing something to facilitate the interaction between its child process(es) and the terminal. That's just not true. Once it has done the setup work (process creation, terminal mode setting, creation of pipes and redirection of other file descriptors, and executing the target program) the shell just waits. What you type into cat
isn't going through the shell, whether it's normal input or a signal-generating special character like ^C
. The cat
process has direct access to the terminal through its own file descriptors, and the terminal has the ability to send signals directly to the cat
process because it's the foreground process group. The shell has gotten out of the way.
After the cat
process dies, the shell will be notified, because it's the parent of the cat
process. Then the shell becomes active and puts itself in the foreground again.
Here is an exercise to increase your understanding.
At the shell prompt in a new terminal, run this command:
exec cat
The exec
keyword causes the shell to execute cat
without creating a child process. The shell is replaced by cat
. The PID that formerly belonged to the shell is now the PID of cat
. Verify this with ps
in a different terminal. Type some random lines and see that cat
repeats them back to you, proving that it's still behaving normally in spite of not having a shell process as a parent. What will happen when you press ^C
now?
Answer:
SIGINT is delivered to the cat process, which dies. Because it was the only process on the terminal, the session ends, just as if you'd said "exit" at a shell prompt. In effect cat was your shell for a while.