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 >pidAnd 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 SigThe 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
fiIf 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 huponexitto enable it. - Execute cmd
shopt -u huponexitto 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 huponexit5 disablehuponexit5 shell option to avoid bash sending background processesSIGHUPsignal on exit.set -m3 enable job control to avoid background processes ignoringSIGQUITandSIGINTsignals.#!/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