睡眠、等待和 Ctrl+C 传播

睡眠、等待和 Ctrl+C 传播

如果我有以下 shell 脚本

sleep 30s

Ctrl+C当 shell 脚本运行时,我点击了它,它sleep随之消失。

如果我有以下 shell 脚本

sleep 30s &
wait

Ctrl+C当 shell 脚本运行时,我点击了,sleep继续,现在父级为 1。

这是为什么? bash 不会传播Ctrl+C给所有孩子吗?

编辑:如果我有以下脚本

/usr/bin/Xvfb :18.0 -ac -screen 0 1180x980x24 &
wait

我正在生成一个程序,这次Ctrl+C在主进程上Xvfb也杀死了该进程。

那么Xvfb与睡眠有何不同/为何不同?

在一些情况下,我发现它们被 回收init,而有些情况下它们会死亡。为什么 sleep 会被 init 回收?为什么会Xvfb死亡?

答案1

太长;博士;Xvfb进程设置了一个信号处理程序,SIGINT并在收到此类信号时退出,但进程sleep没有这样做,因此它继承了“忽略”状态,因为SIGINT它是由在执行二进制文件之前运行脚本的 shell 设置的sleep

运行 shell 脚本时,作业控制将关闭,后台进程(以 开头的进程&)仅在同一进程组中运行,并将SIGINTSIGQUIT设置为SIG_IGN(忽略),并且其标准输入从 重定向/dev/null

这是由标准:

如果在 shell 执行异步列表时禁用作业控制(请参阅 set -m 的说明),则列表中的命令将从 shell 继承 SIGINT 和 SIGQUIT 信号的忽略 (SIG_IGN) 信号操作。

如果信号处理设置为(忽略),则该状态将通过和SIG_IGN继承fork()execve():

在调用过程映像中设置为默认操作 (SIG_DFL) 的信号应在新过程映像中设置为默认操作。除 SIGCHLD 外,调用过程映像设置为忽略的信号 (SIG_IGN) 应设置为由新过程映像忽略。

答案2

来自bash 手册页:

后台进程是指进程组ID与终端进程组ID不同的进程;此类进程不受键盘生成信号的影响

你可以用不同的方式来处理这个问题;首先,终止列出的作业:

#!/bin/bash
trap 'kill $(jobs -p)' INT
sleep 30s &
wait

或者,向同一进程组中的所有进程发送终止命令:

#!/bin/bash
trap 'kill 0' INT
sleep 30s &
wait

答案3

Bash 不会将 SIGINT 或 SIGTERM 等信号转发给当前正在等待的进程。

一种常见的解决方法是执行 trap wait 等待,如以下示例所示:

int_handler()
{
    kill -TERM "${child_pid}" > /dev/null 2>&1
}

trap 'int_handler' INT

echo "Sleeping ... "
sleep 200 &

child_pid=$!
wait ${child_pid} > /dev/null 2>&1
trap - INT
wait ${child_pid} > /dev/null 2>&1

相关内容