Table of Contents
Interesting problem occurred
Recently I use a bash script to launch some processes running in the background. I handle the process termination improperly due to being not familiar with Unix and with Bash. I do something like this.
I write a helper script to launch a background process, which is the “sleep” command in this case.
#!/bin/bash
sleep 1000 &
PID=$!
echo $PID launched
echo $PID >pid
And then I want to use kill -2 $PID
to send the SIGINT
1 to stop this process.
But it does not work at all.
Behind the scenes
After checking the process status file of this process, I find out that it ignores the SIGINT
1 signal.
cat /proc/`cat pid`/status | grep Sig
The value of the "SigIgn"
field of the process status file is 6
, which is 110
in bit representation.
This value means the SIGINT
and SIGQUIT
signals 1 being ignored. 2
SigQ: 0/3793
SigPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000006
SigCgt: 0000000000000000
Run below useful script to see whole procedures.
#!/bin/bash
sleep 1000 &
PID=$!
ps $PID >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
echo Process is alive
fi
cat /proc/$PID/status | grep SigIgn
kill -2 $PID
ps $PID >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
echo Process is still alive
kill $PID
ps $PID >/dev/null 2>&1
if [[ $? -eq 1 ]]; then
echo Process is killed
fi
fi
If we execute the above script ./demo_with_kill.sh
in interactive mode, bash -i ./demo_with_kill.sh
3.
We will see the different result outcomes.
We can see the sleep
process’s "SigIgn"
value being 0
.
This means it can receive SIGINT
1 now.
Process is alive
SigIgn: 0000000000000000
What is the difference between interactive mode and non-interactive mode of bash?
The monitor mode
, aka Job Control
4, is disabled by default in non-interactive mode.
-m
Monitor mode. Job control is enabled. This option is on by default for interactive shells on systems that support it (see JOB CONTROL above). Background processes run in a separate process group and a line containing their exit status is printed upon their completion. — is quoted from “man 1 bash“
Job Control 4
Job control refers to the protocol for allowing a user to move between multiple process groups (or jobs) within a single login session.
Bash runs without it will make background child process ignoring SIGINT
and SIGQUIT
signals 1.
I believe SIGINT
and SIGQUIT
signals 1 are set ignored by below fragment codes come from bash/execute_cmd.c#L5394-L541.
void
setup_async_signals ()
{
#if defined (__BEOS__)
set_signal_handler (SIGHUP, SIG_IGN); /* they want csh-like behavior */
#endif
#if defined (JOB_CONTROL)
if (job_control == 0)
#endif
{
/* Make sure we get the original signal dispositions now so we don't
confuse the trap builtin later if the subshell tries to use it to
reset SIGINT/SIGQUIT. Don't call set_signal_ignored; that sets
the value of original_signals to SIG_IGN. Posix interpretation 751. */
get_original_signal (SIGINT);
set_signal_handler (SIGINT, SIG_IGN);
get_original_signal (SIGQUIT);
set_signal_handler (SIGQUIT, SIG_IGN);
}
}
Bash send SIGHUP on exit
Bash will send all child processes SIGHUP
signal 1 on exit if it being enabled huponexit
5 option.
- Execute cmd
shopt -s huponexit
to enable it. - Execute cmd
shopt -u huponexit
to disable it. 5
It is looked like being disabled by default in my case, not acting like the Orphaned Process Groups | The GNU C Library Reference Manual saying.
Conclusion
So the better way to write a background job script is
shopt -u huponexit
5 disablehuponexit
5 shell option to avoid bash sending background processesSIGHUP
signal on exit.set -m
3 enable job control to avoid background processes ignoringSIGQUIT
andSIGINT
signals.#!/bin/bash shopt -u huponexit set -m
Footnotes
1 24.2.2 Termination Signals | The GNU C Library Reference Manual
2 How to read bitmask for the signals | Red Hat Customer Portal
3 The Set Builtin | bash manual
4 References