eZa*_*oto 5 process signals shell-script interrupt background-process
我在脚本中有以下内容:
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
ps aux | grep yes
Run Code Online (Sandbox Code Playgroud)
当我运行它时,输出显示它yes
在脚本结束时仍在运行。但是,如果我以交互方式运行命令,则进程将成功终止,如下所示:
> yes >/dev/null &
[1] 9967
> kill -INT 9967
> ps aux | grep yes
sean ... 0:00 grep yes
Run Code Online (Sandbox Code Playgroud)
为什么 SIGINT 在交互式实例中终止进程而不在脚本化实例中终止?
编辑
以下是一些可能有助于诊断问题的补充信息。我编写了以下 Go 程序来模拟上述脚本。
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
yes := exec.Command("yes")
if err := yes.Start(); err != nil {
die("%v", err)
}
time.Sleep(time.Second*2)
kill := exec.Command("kill", "-INT", fmt.Sprintf("%d", yes.Process.Pid))
if err := kill.Run(); err != nil {
die("%v", err)
}
time.Sleep(time.Second*2)
out, err := exec.Command("bash", "-c", "ps aux | grep yes").CombinedOutput()
if err != nil {
die("%v", err)
}
fmt.Println(string(out))
}
func die(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
Run Code Online (Sandbox Code Playgroud)
我将它构建为main
并 ./main
在脚本中运行,然后运行./main
并以./main &
交互方式提供相同的以下输出:
sean ... 0:01 [yes] <defunct>
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes
Run Code Online (Sandbox Code Playgroud)
但是,./main &
在脚本中运行会给出以下结果:
sean ... 0:03 yes
sean ... 0:00 bash -c ps aux | grep yes
sean ... 0:00 grep yes
Run Code Online (Sandbox Code Playgroud)
这让我相信差异与 Bash 自己的工作控制关系不大,尽管我在 Bash shell 中运行所有这些。
使用什么 shell 是一个问题,因为不同的 shell 处理作业控制的方式不同(而且作业控制很复杂;根据job.c
,bash
目前在 C 中占 3,300 行cloc
)。例如,Mac OS X 10.11 上的pdksh
5.2.14 与bash
3.2 显示:
$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
$ bash code
38643
38643
$ ksh code
38650
$
Run Code Online (Sandbox Code Playgroud)
这里还相关的是不yes
执行信号处理,因此继承了从父 shell 进程继承的任何内容;相比之下,如果我们确实执行信号处理——
$ cat sighandlingcode
perl -e '$SIG{INT} = sub { die "ouch\n" }; sleep 5' &
pid=$!
sleep 2
kill -INT $pid
$ bash sighandlingcode
ouch
$ ksh sighandlingcode
ouch
$
Run Code Online (Sandbox Code Playgroud)
— SIGINT 的触发与父 shell 无关,因为perl
这里的不同之处yes
改变了信号处理。有一些与信号处理相关的系统调用,可以通过 DTrace 或strace
Linux 上的此处观察到:
-bash-4.2$ cat code
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ rm foo*; strace -o foo -ff bash code
21899
21899
code: line 9: 21899 Terminated yes > /dev/null
-bash-4.2$
Run Code Online (Sandbox Code Playgroud)
我们发现该yes
过程最终SIGINT
被忽略:
-bash-4.2$ egrep 'exec.*yes' foo.21*
foo.21898:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
foo.21899:execve("/usr/bin/yes", ["yes"], [/* 24 vars */]) = 0
foo.21903:execve("/usr/bin/pgrep", ["pgrep", "yes"], [/* 24 vars */]) = 0
foo.21904:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0
-bash-4.2$ grep INT foo.21899
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=21897, si_uid=1000} ---
-bash-4.2$
Run Code Online (Sandbox Code Playgroud)
用perl
代码重复这个测试,你应该看到它SIGINT
没有被忽略,或者pdksh
在bash
. 打开“监视器模式”就像在 中处于交互模式一样bash
,yes
被杀死。
-bash-4.2$ cat monitorcode
#!/bin/bash
set -m
pkill yes
yes >/dev/null &
pid=$!
echo $pid
sleep 2
kill -INT $pid
sleep 2
pgrep yes
pkill yes
-bash-4.2$ ./monitorcode
22117
[1]+ Interrupt yes > /dev/null
-bash-4.2$
Run Code Online (Sandbox Code Playgroud)