我从 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 时等待子进程完成Ctrl,C然后只有在子进程通过 SIGINT 死亡时才决定死亡。