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

Viewed 396

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.

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.

Related