【问题标题】:Why do shells ignore SIGINT and SIGQUIT in backgrounded processes?为什么 shell 在后台进程中忽略 SIGINT 和 SIGQUIT?
【发布时间】:2017-07-14 15:30:41
【问题描述】:

如果我在脚本或 -c sn-p 中后台处理进程,后台进程会忽略 SIGINT 和 SIGQUIT:

例子:

$ 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

这意味着,如果我从交互式父 shell (bash) 运行 kill -INT -$!(或 fg 后跟 Ctrl-C),则从 -c sn-p 后台运行的睡眠进程不会到达并继续存在.

  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

这种行为的原因是什么?可以禁用吗?

【问题讨论】:

  • 后台进程与shell在不同的进程组中。
  • @Barmar 不是。请参阅已发布的 ps 输出。从 -c sn -p 后台处理的进程具有相同的进程组号,但是 SIGINT 和 SIGQUIT 包含在其忽略的信号掩码 (0000000000000006) 中。
  • 来自 bash 手册页(没有动机):When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.
  • @WilliamPursell 这似乎是一个不必要的(可以用(trap '' INT QUIT; exec the_command) 模拟)半途而废(Ctrl-Z 仍然可以到达后台进程)进程组的仿真,但不管是什么原因,我在 POSIX (pubs.opengroup.org/onlinepubs/9699919799/utilities/…) 中找到它,所以我想我必须处理它,不管它有多烦人。
  • @PiotrDobrogost 您希望至少显示一个进程发生的位置和一个没有发生的进程,即:脚本中的 sh -c sleep 1000 & sleep 1000', especially to show the ignore effect is due to what &` 发生而不是由于继承自脚本的父级。在示例中,管道显然是不必要的,但管道链接有点像背景(因为它们将并行化添加到原本概念上的单线程脚本中)所以它并没有太大伤害,IMO,将它扔在那里以显示信号处置是如何的在管道中处理。

标签: linux shell unix signals posix


【解决方案1】:

当 shell 在后台运行程序时,后台进程不应再与原始 shell 绑定——shell 可以退出或被杀死,而后台进程应该继续运行。

如果 shell 是交互式的并且正在使用作业控制,它会将后台进程放在一个单独的进程组中,因此发送到 shell 进程组的信号不会影响它。

但是当不使用作业控制时,这是非交互式 shell 的默认设置,后台进程在同一个进程组中。为了避免后台进程接收到仅用于 shell 的键盘信号,它显然会忽略那些子进程中的这些信号。

【讨论】:

  • 好的,但是您有解决方法吗?如何将 SIGINT 发送到后台进程?我问这个,例如。在 python 中,捕捉 SIGINT 简单易行,但捕捉其他信号就很尴尬,而且有点乱。
  • @drevicko 如果进程建立了自己的信号处理程序,它将替换从 shell 继承的默认设置。因此,如果您的 Python 脚本捕获 SIGINT,您应该不会有任何问题。
  • 这在 Python 中似乎不起作用。我怀疑设置信号处理程序的 Python 方法实际上并没有设置进程信号处理程序,而是一个内部 python 处理程序,使进程处理程序保持不变(假设进程会收到信号!)
  • 这是不可能的。如果 Python 没有设置进程信号处理程序,则默认是终止进程。覆盖任何默认值的唯一方法是设置信号处理程序。
  • 我想有可能Python会检查进程信号处理程序是否设置为SIG_IGN,然后它没有建立自己的,但这似乎有悖常理。也许你应该问一个关于这个的 Python 问题。
猜你喜欢
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 2012-10-08
  • 2016-02-10
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多