This picture shows how “control-c” gets into /dev/ttyS0 (that is, /dev/console).
The serial driver simply sends char stream to n_tty driver. And n_tty search for all the control characters.

When a ctrl-c pushed:
n_tty.c:n_tty_receive_break() –> isig(SIGINT,tty) –> kill_pg(SIGINT, tty->pgrp)
signal.c:kill_pg() calls signal(SIGINT,task) to interrupt every task has group number of tty->pgrp.
So, if a process has the same group id with the tty, it can be kill by “ctrl-c” from that tty, no matter it is in background or foreground. In some old shells without job control feature (such as msh in busybox 1.00), if you start a process in background (with an “&”) then list processes with “ps -j”, you’ll find that the “bg” process has the same PGID with tty. In this situation, ctrl-c can kill both you fg and bg processes.
Job control is a feature that most of shells have, such bash, ash, or lash in busybox. It assigns a new group id to a background process. Most implemented in this way:
setpgid(child->pid, newjob->progs[0].pid);
That means set the new background process’s pgid identical to its pid, which make the process leader of a new process group, and will not be kill by ctrl-c. To these shells, “&” means fork/setpgid.
And this topic leads to another interesting topic: How to write a “standard” daemon?
1. Parent must exit after fork().
2. call setsid. Which makes the child leader of a new process group and a new session,
and detached from the TTY.
3. fork() and exit again, so the new “grandson” process is not a session leader and will never
get TTY control again. This step may be unnecessary.
4. chdir(“/”); optional. To avoid possible umount problem.
4. Redirect handle 0,1,2. That is, close and reopen them to /dev/null or some safe files.
shashidhara D N said
hi
Peter Teoh said
THank you for the graphical and detailed analysis!!!
Hope to see more work done in Linux Kernel in future!!!!