这是一个简单的程序,它注册两个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
SIGINT
和SIGQUIT
在后台进程中被忽略(除非它们在后台打开set -m
)。这是一个(奇怪的)POSIX要求(见 2. 外壳命令语言或我的 SO 问题 为什么外壳在后台进程中忽略 SIGINT 和 SIGQUIT?了解更多详情)。
此外,POSIX 还要求:
进入子壳时,未被忽略的陷阱应为 设置为默认操作,命令除外 仅包含一个陷阱命令的替换..
但是,即使您在重置后再次在子外壳中设置了INT
处理程序,子外壳也无法接收它,因为它被忽略了(您可以尝试一下,也可以使用ps
检查信号忽略掩码,例如)。
后台作业不应该绑定到启动它们的 shell。 如果您退出外壳,它们将继续运行。因此,它们不应该被SIGINT
打断,而不是默认的。启用作业控制后,将自动完成此操作,因为后台作业在单独的进程组中运行。当作业控制被禁用时(通常在非交互式 shell 中),bash
会使异步命令忽略SIGINT
。
文档的相关部分:
由 Bash 启动的非内置命令将信号处理程序设置为外壳从其父级继承的值。当作业控制无效时,异步命令将忽略
SIGINT
和SIGQUIT
以及这些继承的处理程序。作为命令替换的结果运行的命令会忽略键盘生成的作业控制信号SIGTTIN
、SIGTTOU
和SIGTSTP
。
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
更多关于它的信息 这里.