由于信号,bash不会在子进程异常退出时退出

Gab*_*iel 6 bash process signals exit

我真的很努力去理解我做错了什么,为什么?

我有一个launch.sh启动process.sh.

启动文件

#!/bin/bash
while true; do 
./process.sh 
done
Run Code Online (Sandbox Code Playgroud)

进程文件

#!/bin/bash
function signalHandler() {
    for i in {1..2}; do
        sleep 0.1s
        echo "process.sh: cleanup $i"
    done
    exit 130
}

trap "signalHandler" "SIGINT"

while true; do 
sleep 1s
done
Run Code Online (Sandbox Code Playgroud)

当我跑

./launch.sh &
Run Code Online (Sandbox Code Playgroud)

然后用它杀死它

kill -s SIGINT -$!    
Run Code Online (Sandbox Code Playgroud)

where$!获取最后一个命令 (launch.sh) 的 PID,减号将信号发送给所有孩子,然后launch.sh继续。为什么?


我期望以下行为(根据此博客Signal & Bash):

launch.sh在后台运行脚本的外壳 A被中断,Bash 等待process.sh完成。由于process.sh异常返回(出射130)由于在信号处理程序process.sh,外壳A应该退出。为什么不呢?

如果这个孩子的状态表明它由于那个信号而异常退出,shell 会清理,删除它的信号处理程序,并再次杀死自己以触发操作系统默认操作(异常退出)。或者,它按照陷阱设置运行脚本的信号处理程序,然后继续。

mur*_*uru 5

第一,exit 130不是异常退出。是正常退出,退出状态为130。从man 3 wait(POSIX)可以看出:

   If  the  information  pointed  to  by  stat_loc was stored by a call to
   waitpid()  that  specified  the  WUNTRACED     and  WCONTINUED   flags,
   exactly one of the macros WIFEXITED(*stat_loc), WIFSIGNALED(*stat_loc),
   WIFSTOPPED(*stat_loc),  and WIFCONTINUED(*stat_loc)  shall evaluate  to
   a non-zero value.
Run Code Online (Sandbox Code Playgroud)

WIFEXITED检查正常退出,并且WIFSIGNALLED由于未捕获的信号而终止。由于这些是互斥的,所以 anexit 130是正常的。

当进程被 SIGINT 终止时,退出状态为 130 是因为 bash 在进程外将其设置为 130,因为它检测到由于 SIGINT 导致的退出:为什么 bash 设置 $?(退出状态)在 Ctrl-C 或 Ctrl-Z 上为非零?

其次,处理 SIGINT 然后死亡的进程应该用 SIGINT 杀死自己。Greg 的 wiki(shell 内容的极好资源)对此有注释

如果您选择为 SIGINT 设置处理程序(而不是使用 EXIT 陷阱),您应该意识到响应 SIGINT 退出的进程应该使用 SIGINT 杀死自己,而不是简单地退出,以避免给调用者带来问题。因此:

trap 'rm -f "$tempfile"; trap - INT; kill -INT $$' INT
Run Code Online (Sandbox Code Playgroud)

现在,如果您将其更改exit 130为:

trap - INT
kill -INT $$
Run Code Online (Sandbox Code Playgroud)

你会看到预期的行为。