polyglot
polyglot

Reputation: 2058

How does trap / kill work in bash on Linux?

My sample file

traptest.sh:

#!/bin/bash
trap 'echo trapped' TERM
while :
do
  sleep 1000
done

$ traptest.sh &

[1] 4280

$ kill %1 <-- kill by job number works

Terminated

trapped

$ traptest.sh &

[1] 4280

$ kill 4280 <-- kill by process id doesn't work?

(sound of crickets, process isn't killed)

If I remove the trap statement completely, kill process-id works again?

Running some RHEL 2.6.18-194.11.4.el5 at work. I am really confused by this behaviour, is it right?

Upvotes: 16

Views: 27905

Answers (3)

Adrian
Adrian

Reputation: 675

Davide Berra explained the difference between kill %<jobspec> and kill <PID>, but not how that difference results in what you observed. After all, Unix signal handlers should be called pretty much instantaneously, so why does sending a SIGTERM to the script alone not trigger its trap handler?

The bash man page explains why, in the last paragraph of the SIGNALS section:

If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes.

So, the signal was delivered immediately, but the handler execution was deferred until sleep exited.

Hence, with kill %<jobspec>:

  • Both the script and sleep received SIGTERM
  • bash registered the signal, noticed that a trap was set for it, and queued the handler for future execution
  • sleep exited immediately
  • bash noted sleep's exit, and ran the trap handler

whereas with kill <script_PID>:

  • Only the script received SIGTERM
  • bash registered the signal, noticed that a trap was set for it, and queued the handler for future execution
  • sleep exited after 1000 seconds
  • bash noted sleep's exit, and ran the trap handler

Obviously, you didn't want long enough to see that last bit. :)


If you're interested in the gory details, download the bash source code and look in trap.c, specifically the trap_handler() and run_pending_traps() functions.

Upvotes: 6

Davide Berra
Davide Berra

Reputation: 6568

kill [pid]

send the TERM signal exclusively to the specified PID.

kill %1

send the TERM signal to the job #1's entire process group, in this case to the script pid + his children (sleep).

I've verified that with strace on sleep process and on script process

Anyway, someone got a similar problem here (but with SIGINT instead of SIGTERM): http://www.vidarholen.net/contents/blog/?p=34.

Quoting the most important sentence:

kill -INT %1 sends the signal to the job’s process group, not the backgrounded pid!

Upvotes: 12

davak
davak

Reputation: 313

This is expected behavior. Default signal sent by kill is SIGTERM, which you are catching by your trap. Consider this:

#!/bin/bash
# traptest.sh

trap "echo Booh!" SIGINT SIGTERM
echo "pid is $$"

while :                 # This is the same as "while true".
do
    a=1
done

(sleep really creates a new process and the behavior is clearer with my example I guess).

So if you run traptest.sh in one terminal and kill TRAPTEST_PROCESS_ID from another terminal, output in the terminal running traptest will be Booh! as expected (and the process will NOT be killed). If you try sending kill -s HUP TRAPTEST_PROCESS_ID, it will kill the traptest process.

This should clear up the %1 confusion.

Note: the code example is taken from tldp

Upvotes: 8

Related Questions