Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the KILL signal handler not executing when my child process dies

Tags:

bash

I have migrated some scripts from ksh to bash and I have observed very strange behavior in bash. I was able to reduce to a very short snippet.

echo first test
LC_ALL=C xclock &
Active_pid=$!

sleep 1

kill -9 $Active_pid
sleep 1

echo second test
LC_ALL=C xclock &
Active_pid=$!

sleep 1
trap "echo Signal SIGKILL caught"  9
kill -9 $Active_pid
sleep 1

The output is

first test
./mig_bash.sh: line 15:  4471 Killed                  LC_ALL=C xclock
second test

My problem was the production of the trace in the first test. I have tried to see if a signal was received. By trial and error, I wrote the "second test" that solve my problem. I do not understand it. How this removes the trace of the first test without executing echo Signal SIGKILL ?

I am completely lost.

like image 412
BOC Avatar asked Oct 06 '17 13:10

BOC


People also ask

Can a process ignore SIGTERM?

The SIGTERM signal is a generic signal used to cause program termination. Unlike SIGKILL , this signal can be blocked, handled, and ignored.

Does SIGTERM propagate to child processes?

There is no automatic propagation of signals (SIGTERM or otherwise) to children in the process tree.

What signal number do you send to forcefully kill a process?

The Kill menu item enables you to kill a process quickly by sending it a kill (9) signal.

Which signal is used to kill the given process?

kill ends a process by sending it a signal. The default signal is SIGTERM. kill is a built-in shell command.


1 Answers

I couldn't find anything in the bash documentation that would explain the observed behavior, so I turned to the source code. Debugging lead to the function notify_of_job_status(). The line that prints the message about a killed subprocess can be reached only if all of the following conditions hold:

  1. the subprocess is registered in the job table (i.e. has not been disown-ed)
  2. the shell was NOT started in interactive mode
  3. the signal that terminated the child process is NOT trapped in the parent shell (see the signal_is_trapped (termsig) == 0 check)

Demonstration:

$ cat test.sh 
echo Starting a subprocess
LC_ALL=C sleep 100 &
Active_pid=$!
case "$1" in
    disown)      disown ;;
    trapsigkill) trap "echo Signal SIGKILL caught"  9 ;;
esac
sleep 1
kill -9 $Active_pid
sleep 1
echo End of script

$ # Demonstrate the undesired message
$ bash test.sh 
Starting a subprocess
test.sh: line 14: 15269 Killed                  LC_ALL=C sleep 100
End of script

$ # Suppress the undesired message by disowning the child process
$ bash test.sh disown
Starting a subprocess
End of script

$ # Suppress the undesired message by trapping SIGKILL in the parent shell
$ bash test.sh trapsigkill
Starting a subprocess
End of script

$ # Suppress the undesired message by using an interactive shell
$ bash -i test.sh 
Starting a subprocess
End of script

How this removes the trace of the first test without executing echo Signal SIGKILL ?

The trap is not executed since the KILL signal is received by the sub-process rather than the shell process for which the trap has been set. The effect of the trap on the diagnostics is in the (somewhat arguable) logic in the notify_of_job_status() function.

like image 82
Leon Avatar answered Oct 25 '22 07:10

Leon