bash:为什么我不能在背景外壳中为 SIGINT 设置陷阱?



这是一个简单的程序,它注册两个trap处理程序,然后用trap -p显示它们。 然后它做同样的事情,但在子后台进程中。

为什么后台进程忽略SIGINT陷阱?

#!/bin/bash
echo "Traps on startup:"
trap -p
echo ""
trap 'echo "Received INT"' INT
trap 'echo "Received TERM"' TERM
echo "Traps set on parent:"
trap -p
echo ""
(
echo "Child traps on startup:"
trap -p
echo ""
trap 'echo "Child received INT"' INT
trap 'echo "Child received TERM"' TERM
echo "Traps set on child:"
trap -p
echo ""
) &
child_pid=$!
wait $child_pid

输出:

$ ./show-traps.sh
Traps on startup:
Traps set on parent:
trap -- 'echo "Received INT"' SIGINT
trap -- 'echo "Received TERM"' SIGTERM
Child traps on startup:
Traps set on child:
trap -- 'echo "Child received TERM"' SIGTERM

SIGINTSIGQUIT在后台进程中被忽略(除非它们在后台打开set -m)。这是一个(奇怪的)POSIX要求(见 2. 外壳命令语言或我的 SO 问题 为什么外壳在后台进程中忽略 SIGINT 和 SIGQUIT?了解更多详情)。

此外,POSIX 还要求:

进入子壳时,未被忽略的陷阱应为 设置为默认操作,命令除外 仅包含一个陷阱命令的替换..

但是,即使您在重置后再次在子外壳中设置了INT处理程序,子外壳也无法接收它,因为它被忽略了(您可以尝试一下,也可以使用ps检查信号忽略掩码,例如)。

后台作业不应该绑定到启动它们的 shell。 如果您退出外壳,它们将继续运行。因此,它们不应该被SIGINT打断,而不是默认的。启用作业控制后,将自动完成此操作,因为后台作业在单独的进程组中运行。当作业控制被禁用时(通常在非交互式 shell 中),bash会使异步命令忽略SIGINT

文档的相关部分:

由 Bash 启动的非内置命令将信号处理程序设置为外壳从其父级继承的值。当作业控制无效时,异步命令将忽略SIGINTSIGQUIT以及这些继承的处理程序。作为命令替换的结果运行的命令会忽略键盘生成的作业控制信号SIGTTINSIGTTOUSIGTSTP

https://www.gnu.org/software/bash/manual/html_node/Signals.html

为了便于实现用户界面到作业控制,操作系统维护当前终端进程组 ID 的概念。此进程组的成员(进程组 ID 等于当前终端进程组 ID)接收键盘生成的信号,例如SIGINT。据说这些过程处于前台。后台进程是其进程组 ID 与终端的进程组 ID 不同的进程;此类进程不受键盘生成的信号的影响。仅允许前台进程读取,或者,如果用户使用 stty tostop 指定,则写入终端。尝试从终端读取(当 stty tostop 生效时写入)的后台进程由内核的终端驱动程序发送SIGTTIN(SIGTTOU)信号,除非被捕获,否则会暂停进程。

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

更多关于它的信息 这里.

最新更新