The easiest thing to understand Linux terminal devices (
/dev/pts/*) is as you think on them as streaming sockets. As if the
/dev/tty12 would be a TCP port, like
Processes are connecting them, get input from them, write into them, and then finally disconnect them. A socket (terminal) can have a connection with multiple processes.
Other processes can listen on them, they are typically the terminal emulator programs, but not always. In the case of the character terminals, the Linux kernel itself works as a "listener daemon" on the terminal device.
The "extra feature" what a terminal has, but a socket hasn't: the kernel keeps track which processes are connecting it, and it is capable to signal them on need. So happens it, for example, if you press ctrl/c.
A detailed list of the passed signals you can read in this answer.
What exactly happens if you press ctrl/c?
It is not if somebody presses a normal button, for example, an "a" key. In this case, the pressed key is simply written into the terminal device and can be read out by the processes reading from it (which is typically the process in the foreground).
In the case of such an extraordinary keypress, for example, ctrl/z, or you close/resize the terminal window, and so on, the terminal gets the request and the kernel sends to all processes attached to it the corresponding signal. To all of them, it will be later important.
These devices can be also controlled by well-directed
If bash runs a subprocess (i.e. external command), then the following happen:
- bash starts the process in the background, giving to it the terminal
- bash stops getting input from the terminal
- after the process stops, bash sets everything back.
However, the bash remains still attached to the terminal device, and if a ctrl/c is coming, it will get the signal. Also the external command will get the signal.
However, these signals can be overridden (
sigaction() system calls). Bash overrides them, i.e. it overrides the default signal handling routine (which would simply stop it) with its own. This is why it doesn't exit if you press the ctrl/c in a command prompt.
sleep 60 will exit. It doesn't change its signal handlers.
If you run a bash internal command, this signal handler will work as you said (it stops the internal command execution and gets you back to the prompt).