bash: 为什么我不能在后台设置 SIGINT 陷阱 shell?

bash: Why can't I set a trap for SIGINT in a background shell?

这是一个简单的程序,它注册了两个 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. Shell 命令语言 或我的 SO 问题 Why do shells ignore SIGINT and SIGQUIT in backgrounded processes? 了解更多详情)。

此外,POSIX 要求:

When a subshell is entered, traps that are not being ignored shall be set to the default actions, except in the case of a command substitution containing only a single trap command ..

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

后台作业 not supposed 与启动它们的 shell 相关联。如果您退出 shell,他们将继续 运行。因此,它们不应被 SIGINT 打断,默认情况下不会。当启用作业控制时,它会自动完成,因为后台作业 运行 在单独的进程组中。当禁用作业控制时(通常在非交互式 shells 中),bash 使异步命令忽略 SIGINT.

文档的相关部分:

Non-builtin commands started by Bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers. Commands run as a result of command substitution ignore the keyboard-generated job control signals SIGTTIN, SIGTTOU, and SIGTSTP.

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

To facilitate the implementation of the user interface to job control, the operating system maintains the notion of a current terminal process group ID. Members of this process group (processes whose process group ID is equal to the current terminal process group ID) receive keyboard-generated signals such as SIGINT. These processes are said to be in the foreground. Background processes are those whose process group ID differs from the terminal’s; such processes are immune to keyboard-generated signals. Only foreground processes are allowed to read from or, if the user so specifies with stty tostop, write to the terminal. Background processes which attempt to read from (write to when stty tostop is in effect) the terminal are sent a SIGTTIN (SIGTTOU) signal by the kernel’s terminal driver, which, unless caught, suspends the process.

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

更多内容here