向进程组发送 SIGINT 有时会被忽略

向进程组发送 SIGINT 有时会被忽略

我从 bash 启动一个进程组。然后我向整个进程组发送 SIGINT。有时 SIGINT 会杀死进程,有时不会。为什么 SIGINT 有时会被忽略?

我看到不同的行为,具体取决于进程组是否在后台启动、bash shell 的嵌套以及 Mac/Linux 操作系统。如果有人能阐明这一点,我将非常感激。

在以下示例中,我使用名为的 python 可执行文件sleep_in_pgrp.py

#!/usr/bin/env python2.7
import os;
import subprocess
os.setpgrp();
subprocess.check_call(["sleep","10000"]);

它创建一个进程组并开始睡眠。观察到的现象应该与python无关。我使用 python 只是因为 bash 没有setpgrp 命令或内置命令。更新:显然还可以运行交互式 shell 来创建一个新进程组

1)在后台启动进程组并等待leader。 SIGINT 被忽略。

执行以下命令:

$ bash -c '  { sleep_in_pgrp.py; } & wait $!  '

Bash 在后台启动 python 并等待它。在另一个终端中:

$ ps -Heo pid,ppid,tpgid,pgid,sid,user,args
   PID   PPID  TPGID   PGID    SID     COMMAND
  2507   1574   2963   2507   2507     -bash
  2963   2507   2963   2963   2507       bash -c   { sleep_in_pgrp.py; } & wait $!
  2964   2963   2963   2963   2507         bash -c   { sleep_in_pgrp.py; } & wait $!
  2965   2964   2963   2965   2507           python2.7 ./sleep_in_pgrp.py
  2966   2965   2963   2965   2507             sleep 10000

SIGINT'ing python 的进程组不杀死任何进程。可能是什么原因?

$ sudo kill -s SIGINT -- -2965

2) 在前台启动进程组。信号情报有效。

如果我删除& wait $!,SIGINT 将按预期杀死进程组。我不知道为什么,但我并不感到惊讶 SIGINT 在这种情况下杀死了进程。

$ bash -c '  { sleep_in_pgrp.py; }  '

在另一个终端中:

$ ps -Heo pid,ppid,tpgid,pgid,sid,user,args
   PID   PPID  TPGID   PGID    SID     COMMAND
  2507   1574   3352   2507   2507     -bash
  3352   2507   3352   3352   2507       bash -c   { sleep_in_pgrp.py; }
  3353   3352   3352   3353   2507         python2.7 ./sleep_in_pgrp.py
  3354   3353   3352   3353   2507           sleep 10000

SIGINT 终止进程组。

$ sudo kill -s SIGINT -- -3353

3)在后台运行python时删除子shell。信号情报有效。

我非常惊讶 shell 嵌套会影响这里的行为。我想不出任何解释。

bash -c在开始时删除了:

$ { sleep_in_pgrp.py; } & wait $!

在另一个终端中:

$ ps -Heo pid,ppid,tpgid,pgid,sid,user,args
   PID   PPID  TPGID   PGID    SID     COMMAND
  2507   1574   2507   2507   2507     -bash
  3488   2507   2507   3488   2507       -bash
  3489   3488   2507   3489   2507         python2.7 ./sleep_in_pgrp.py
  3490   3489   2507   3489   2507           sleep 10000

SIGINT 终止进程组。

$ sudo kill -s SIGINT -- -2507

4) 在 Mac 中运行第一个命令:SIGINT 有效。

前 2 个命令在 CentOs7 虚拟机中运行。

$ uname -a
Linux ip-10-229-193-124 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 13 10:46:25 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux

我现在在 mac 的子 shell 中使用后台 python 执行第一个命令。

$ uname -a
Darwin mbp-005063 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64

在苹果机中:

$ bash -c '  { sleep_in_pgrp.py; } & wait $!  '

在另一个终端中:

$   PID  PPID TPGID  PGID   SESS COMMAND
18741 40096 18741 18741      0 bash -c   { sleep_in_pgrp.py; } & wait $!
18742 18741 18741 18741      0 bash -c   { sleep_in_pgrp.py; } & wait $!
18743 18742 18741 18743      0 /usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python ./sleep_in_pgrp.py
18744 18743 18741 18743      0 sleep 10000
40094  2423 18741 40094      0 /Applications/iTerm.app/Contents/MacOS/iTerm2 --server /usr/bin/login -fpl hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
40095 40094 18741 40095      0 /usr/bin/login -fpl hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
40096 40095 18741 40096      0 -bash
-+= 00001 root /sbin/launchd
 \-+= 02423 hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2
   \-+= 40094 hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2 --server /usr/bin/login -fpl hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
     \-+= 40095 root /usr/bin/login -fpl hbaba /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
       \-+= 40096 hbaba -bash
         \-+= 18741 hbaba bash -c   { sleep_in_pgrp.py; } & wait $!
           \-+- 18742 hbaba bash -c   { sleep_in_pgrp.py; } & wait $!
             \-+= 18743 hbaba /usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python ./sleep_in_pgrp.py
               \--- 18744 hbaba sleep 10000

在这种情况下,SIGINT也会杀死进程组

$ sudo kill -s INT -18743

CentOs7 中的 Bash 版本是

$ echo $BASH_VERSION
4.2.46(2)-release

在 Mac 中,bash 版本是

$ echo $BASH_VERSION
4.4.12(1)-release

回答解释 Ctrl+如何C将 SIGINT 发送到进程组。这就是我在这里尝试向进程组发送 SIGINT 的操作。这回答提到非交互式作业需要 SIGINT 处理程序。我不确定它可以解释我所看到的不同行为。我还想知道等待后台进程是否会影响该进程的 SIGINT 处理。

答案1

该进程很可能捕获 SIGINT 信号以与其他函数一起使用。

仅当接收到的信号未知时,该进程才会死亡。但是,如果进程设置了一个链接到该信号的函数,它就不会死,除非链接到该信号的函数完成程序。

例如,一个简单的 C 程序:

 #include <signal.h>

 int sigreceived = 0;

 void mysignal();

 int main(){
    signal(2, mysignal); //SIGINT corresponds to 2 signal

    while(1);
    return 0;
  }

 void mysignal(){
  sigreceived=1;
 }

在这个程序中,捕获信号2是为了调用mysignal函数什么的,而不是杀死进程,只需更改一个变量的值

然后,这个进程不会被 SIGINT 杀死

答案2

请记住,SIGINT 无论是由“ kill -INT pid”还是“ kill -INT -- -pgid”或Ctrl-发送C,总是影响前台进程(前台进程组);使用 & 异步启动的所有内容都不受 INT 影响(除了在后台运行的子 shell,如命令替换:(cmd;cmd, ...etc ) &$(cmd) &)。

不要忘记:bash 实现了 WCE(等待和协作退出),这意味着父进程在通过 - 接收到 SIGINT 时等待子进程完成CtrlC然后只有在子进程通过 SIGINT 死亡时才决定死亡。

比我更好的解释是https://www.cons.org/cracauer/sigint.html

相关内容