19

If I background a processes in a script or a -c snippet, the backgrounded processes ignores 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), the sleep processes backgrounded from the -c snippet isn't reached and survives.

  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?

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 1
    The background process is in a different process group from the shell. – Barmar Jul 14 '17 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). – Petr Skocik Jul 14 '17 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.` – William Pursell Jul 14 '17 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 (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html) so I guess I'll have to deal with it, no matter how annoying it is. – Petr Skocik Jul 14 '17 at 17:20
  • Do we really need three processes and a pipe to demonstrate this? What would be the simplest example? – Piotr Dobrogost Nov 24 '20 at 21:21
  • 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. – Petr Skocik Nov 24 '20 at 21:42

1 Answers1

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.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    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 Mar 08 '18 at 16:34
  • 1
    @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`. – Barmar Mar 08 '18 at 16:40
  • 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!) – drevicko Mar 08 '18 at 16:56
  • 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. – Barmar Mar 08 '18 at 17:01
  • 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. – Barmar Mar 08 '18 at 17:02
  • I suspect Python doesn't even do that... I just tried it out - SIGINT doesn't get through even after setting a handler in the normal python way. – drevicko Mar 08 '18 at 17:03
  • I can't think of how that would work. There isn't an ignore flag. `SIG_IGN` is just a special signal handler that you can set; if a program sets its own signal handler, it replaces that, and the signal won't be ignored. – Barmar Mar 08 '18 at 17:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/166518/discussion-between-drevicko-and-barmar). – drevicko Mar 09 '18 at 09:02
  • _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._ Then why in such a case the background process still have SIGINT and SIGQUIT masked? Tested with `{ seq 100000000 & } | less +F` in one terminal and `ps -O ppid,pgrp,sid,ignored --forest -C bash,seq,less` in another with bash 4.4.12. – Piotr Dobrogost Nov 25 '20 at 18:47