sleep, wait and Ctrl+C propagation
Solution 1
tl;dr; the Xvfb
process sets a signal handler for SIGINT
and exits when it receives such a signal, but the sleep
process doesn't, so it inherits the "ignore" state for SIGINT
as it was set by the shell running the script before executing the sleep
binary.
When a shell script is run, the job control is turned off, and background processes (the ones started with &
) are simply run in the same process group, with SIGINT
and SIGQUIT
set to SIG_IGN
(ignored) and with their stdin redirected from /dev/null
.
This is required by the standard:
If job control is disabled (see the description of set -m) when the shell executes an asynchronous list, the commands in the list shall inherit from the shell a signal action of ignored (SIG_IGN) for the SIGINT and SIGQUIT signals.
If the signal disposition is set to SIG_IGN
(ignore), that state will be inherited through fork()
and execve()
:
Signals set to the default action (SIG_DFL) in the calling process image shall be set to the default action in the new process image. Except for SIGCHLD, signals set to be ignored (SIG_IGN) by the calling process image shall be set to be ignored by the new process image.
Solution 2
From the bash man page:
Background processes are those whose process group ID differs from the terminal’s; such processes are immune to keyboard-generated signals
You could handle this in different ways; first, to kill the listed jobs:
#!/bin/bash
trap 'kill $(jobs -p)' INT
sleep 30s &
wait
Alternatively, send a kill to all of the processes in the same process group:
#!/bin/bash
trap 'kill 0' INT
sleep 30s &
wait
Solution 3
Bash does not forward signals like SIGINT or SIGTERM to processes it is currently waiting on.
One common workaround is to do a trap wait wait as shown in the following example:
int_handler()
{
kill -TERM "${child_pid}" > /dev/null 2>&1
}
trap 'int_handler' INT
echo "Sleeping ... "
sleep 200 &
child_pid=$!
wait ${child_pid} > /dev/null 2>&1
trap - INT
wait ${child_pid} > /dev/null 2>&1
Related videos on Youtube
Hari Sundararajan
Updated on September 18, 2022Comments
-
Hari Sundararajan over 1 year
If I have the following shell script
sleep 30s
And I hit Ctrl+C when the shell script is running, the
sleep
dies with it.If I have the following shell script
sleep 30s & wait
And I hit Ctrl+C when the shell script is running, the
sleep
continues on, and now has a parent of 1.Why is that? Doesn't bash propagate Ctrl+C to all the children?
EDIT: If I have the following script
/usr/bin/Xvfb :18.0 -ac -screen 0 1180x980x24 & wait
where I am spawning a program, this time Ctrl+C on the main process kills the
Xvfb
process too.So how/why is
Xvfb
different from sleep?In the case of some processes I see that they get reaped by
init
, in some cases they die. Why does sleep get reaped by init? Why doesXvfb
die? -
Hari Sundararajan over 5 yearsThat doesn't always seem to be the case though. For instance, if I have this in my shell script
usr/bin/Xvfb :18.0 -ac -screen 0 1180x980x24 &; wait
and I hit Ctrl+C on the parent process, it does call Xvfb too. Why is that? By this logic that process should continue to live, no? -
Hari Sundararajan over 5 yearsWhy does it appear to forward signals when I run certain other commands , such as Xvfb?
-
fpmurphy over 5 years@HariSundararajan. Are you executing your script remotely through
putty
or similar? -
Hari Sundararajan over 5 yearsno, I am sitting in front of the linux box, with the kvm attached to it, and running commands on an xterm.
-
Stéphane Chazelas over 5 yearsPOSIX also forbid shells to allow undoing a SIG_IGN.
zsh
ignores that (unhelpful) requirement, so inzsh
, you can work around it with(trap - INT; sleep 30) &
. In other shells, you'd need to usezsh -c 'trap - INT; exec sleep 30' &
or useperl
or other things that don't have that silly requirement. -
Stéphane Chazelas over 5 yearsIn recent versions of
bash
, the problem can be worked around by starting the command as acoproc
instead of with&
:{ coproc sleep 30 < /dev/null >&4 4>&-; } 4>&1
which doesn't seem to ignore SIGINT/SIGQUIT...