22

If I background processes in a script or a -c snippet, the backgrounded processes ignore SIGINT and SIGQUIT:

Example:

$ alias ps='ps -o pid,ppid,pgrp,sid,stat,tty,ignored,blocked,caught,wchan,min_flt,pmem,args --forest' $ sh -c 'sleep 1000 & sleep 1000 | sleep 1000' & \ sleep 0.01; ps |grep -v -e ps -e grep PID PPID PGRP SID STAT TT IGNORED BLOCKED CAUGHT WCHAN MINFL %MEM COMMAND 6197 2143 6197 6197 Ss pts/28 0000000000380004 0000000000010000 000000004b817efb wait 10039 0.0 -bash 7593 6197 7593 6197 S pts/28 0000000000000000 0000000000000000 0000000000010002 wait 148 0.0 \_ sh -c sleep 1000 & sleep 1000 | sleep 1000 7595 7593 7593 6197 S pts/28 0000000000000006 0000000000000000 0000000000000000 hrtime 85 0.0 | \_ sleep 1000 7596 7593 7593 6197 S pts/28 0000000000000000 0000000000000000 0000000000000000 hrtime 85 0.0 | \_ sleep 1000 7597 7593 7593 6197 S pts/28 0000000000000000 0000000000000000 0000000000000000 hrtime 85 0.0 | \_ sleep 1000 

This means that if I run kill -INT -$! (or fg followed by Ctrl-C) from the interactive parent shell (bash), sleep processes backgrounded from the -c snippet are not reached and survive.

 PID PPID PGRP SID STAT TT IGNORED BLOCKED CAUGHT WCHAN MINFL %MEM COMMAND 6197 2143 6197 6197 Ss pts/28 0000000000380004 0000000000010000 000000004b817efb wait 10103 0.0 -bash 7595 1 7593 6197 S pts/28 0000000000000006 0000000000000000 0000000000000000 hrtime 85 0.0 sleep 1000 

What is the reason for this behavior? Can it be disabled?

6
  • 1
    The background process is in a different process group from the shell. Commented Jul 14, 2017 at 15:34
  • 6
    @Barmar It's not. See the posted ps output. The process backgrounded from the -c snippet has the same process group number, however, SIGINT and SIGQUIT are included in its ignored signal mask (0000000000000006). Commented Jul 14, 2017 at 15:34
  • 4
    From the bash manpage (without motivation): When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers. Commented Jul 14, 2017 at 17:12
  • 4
    @WilliamPursell It's seems like an unecessary (emulatable with (trap '' INT QUIT; exec the_command) ) half-assed (Ctrl-Z can still reach the background process) emulation of processes group, but whatever the reason for it is, I found it in POSIX (pubs.opengroup.org/onlinepubs/9699919799/utilities/…) so I guess I'll have to deal with it, no matter how annoying it is. Commented Jul 14, 2017 at 17:20
  • 2
    @PiotrDobrogost You want to show at least one process where it does happen and one where it doesn't, i.e.: sh -c sleep 1000 & sleep 1000', especially to show the ignore effect is due to what &` in a script does and not due to inheritance from the script's parent. The pipeline is clearly unnecessary in the example, but pipeline links are somewhat like backgrounding (in that they add pararelization to an otherwise conceptually singlethreaded script) so it doesn't hurt much, IMO, to throw it in there to show how signal dispositions are handled in a pipeline. Commented Nov 24, 2020 at 21:42

1 Answer 1

12

When a shell runs a program in the background, the background process is not supposed to be tied to the original shell any more -- the shell can exit or be killed, and the background process should continue running.

If the shell is interactive and job control is being used, it puts the background process in a separate process group, so signals sent to the shell process group don't affect it.

But when job control is not being used, which is the default in non-interactive shells, the background process is in the same process group. To avoid the background process receiving keyboard signals that are just intended for the shell, it apparently ignores those signals in those child processes.

Sign up to request clarification or add additional context in comments.

9 Comments

ok, but do you have a workaround? How can I send SIGINT to a background process? I ask this, as eg. in python, catching SIGINT is simple and easy, but other signals are awkward and a bit messy to catch.
@drevicko If a process establishes its own signal handlers, that will replace the default setting inherited from the shell. So you shouldn't have any problem if your Python script catches SIGINT.
This doesn't appear to work in Python. I suspect the Python approach to setting a signal handler doesn't actually set the processes signal handler, but an internal python one, leaving the process handler untouched (with the assumption the process will get signals!)
That's not possible. If Python doesn't set the process signal handler, the default would be to kill the process. The only way to override any default is to set a signal handler.
I suppose it's possible that Python checks whether the process signal handler is set to SIG_IGN, and then it doesn't establish its own, but that seems perverse. Maybe you should ask a Python question about this.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.