shell process signals – Why is SIGINT Not Propagated to Child Process When Sent to Its Parent Process?

processshellsignals

Given a shell process (e.g. sh) and its child process (e.g. cat), how can I simulate the behavior of Ctrl+C using the shell's process ID?


This is what I've tried:

Running sh and then cat:

[user@host ~]$ sh
sh-4.3$ cat
test
test

Sending SIGINT to cat from another terminal:

[user@host ~]$ kill -SIGINT $PID_OF_CAT

cat received the signal and terminated (as expected).

Sending the signal to the parent process does not seem to work. Why is the signal not propagated to cat when sent to its parent process sh?

This does not work:

[user@host ~]$ kill -SIGINT $PID_OF_SH

Best Answer

How CTRL+C works

The first thing is to understand how CTRL+C works.

When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).
The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty -a and looking at intr = ^C;. The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.

What is the foreground process group?

So, now the question is, how do you determine what the foreground process group is? The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTP, SIGINT, etc).

Simplest way to determine the process group ID is to use ps:

ps ax -O tpgid

The second column will be the process group ID.

How do I send a signal to the process group?

Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.

This can be done with kill by putting a - in front of the group ID.
For example, if your process group ID is 1234, you would use:

kill -INT -1234

Simulate CTRL+C using the terminal number.

So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?

This becomes very easy.

Lets assume $tty is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##' in the terminal).

kill -INT -$(ps h -t $tty -o tpgid | uniq)

This will send a SIGINT to whatever the foreground process group of $tty is.  

Related Question