Kill -INT 无法处理设置了陷阱 INT 的 bash 脚本
Kill -INT not working on bash script with trap INT set
考虑以下 bash 脚本 s
:
#!/bin/bash
echo "Setting trap"
trap 'ctrl_c' INT
function ctrl_c() {
echo "CTRL+C pressed"
exit
}
sleep 1000
调用./s
时,它会休眠。如果您按 Ctrl+C,它会打印消息并退出。
现在调用它,打开另一个终端并杀死相应的bash pid
-INT 标志。它不起作用或做任何事情。为什么?
不带标志的 Kill 有效,但不会调用 ctrl_c() 函数。
当我们使用ps aux 命令查找PID 值时,我们看到有2 个进程,this program 和sleep。程序过程不会像你说的那样用 -INT 参数中断。但是,当睡眠进程使用-2或-INT(同义)参数时,进程如期中断。消息打印出来 exits.I 没看懂为什么主进程没有停止,但是我想表明这个方法可以打断sleep进程。希望对你有好处。
sleep
不是 shell 内置:
$ which sleep
/bin/sleep
这意味着当你 运行 sleep
它将 运行 作为一个单独的子进程。当一个子进程被创建时,各种东西从父进程复制到子进程(inherited),其中之一就是信号掩码。注意:只有面具。所以如果你设置了:
trap '' INT
然后 sleep
会继承那个 - 这意味着忽略信号。但是,在您的情况下,您希望 sleep
(可能是用 C 编写的)执行 bash
函数。没有办法 - 一开始就用错了语言。想象一下:它必须执行 bash
才能执行 bash 函数。
警告 - 有关信号处理的详细信息可能因平台而异。
可以使 sleep
成为内置函数,请参阅 here 但这可能比它的价值更麻烦。
来自the POSIX standard for the shell:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
为了验证这一点,我们可以 运行 strace
在 shell 上执行您的脚本。从另一个终端发送 SIGINT
后,bash 只是注意到信号已被接收并且 returns 等待其子命令 sleep 命令完成:
rt_sigaction(SIGINT, {0x808a550, [], 0}, {0x80a4600, [], 0}, 8) = 0
waitpid(-1, 0xbfb56324, 0) = ? ERESTARTSYS
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = -1 EINTR (Interrupted system call)
waitpid(-1,
要使kill
具有与Ctrl-C相同的效果,您应该将SIGINT
发送到进程组。默认情况下 shell 会将新命令的每个进程放入其自己的 pgrp:
$ ps -f -o pid,ppid,pgid,tty,comm -t pts/1
PID PPID PGID TT COMMAND
3460 3447 3460 pts/1 bash
29087 3460 29087 pts/1 \_ foo.sh
29120 29087 29087 pts/1 \_ sleep
$ kill -2 -29087
现在陷阱 运行 和 shell 退出:
waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 29120
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = 29120
...
write(1, "CTRL+C pressed\n", 15) = 15
...
exit_group(0) = ?
考虑以下 bash 脚本 s
:
#!/bin/bash
echo "Setting trap"
trap 'ctrl_c' INT
function ctrl_c() {
echo "CTRL+C pressed"
exit
}
sleep 1000
调用./s
时,它会休眠。如果您按 Ctrl+C,它会打印消息并退出。
现在调用它,打开另一个终端并杀死相应的bash pid -INT 标志。它不起作用或做任何事情。为什么?
不带标志的 Kill 有效,但不会调用 ctrl_c() 函数。
当我们使用ps aux 命令查找PID 值时,我们看到有2 个进程,this program 和sleep。程序过程不会像你说的那样用 -INT 参数中断。但是,当睡眠进程使用-2或-INT(同义)参数时,进程如期中断。消息打印出来 exits.I 没看懂为什么主进程没有停止,但是我想表明这个方法可以打断sleep进程。希望对你有好处。
sleep
不是 shell 内置:
$ which sleep
/bin/sleep
这意味着当你 运行 sleep
它将 运行 作为一个单独的子进程。当一个子进程被创建时,各种东西从父进程复制到子进程(inherited),其中之一就是信号掩码。注意:只有面具。所以如果你设置了:
trap '' INT
然后 sleep
会继承那个 - 这意味着忽略信号。但是,在您的情况下,您希望 sleep
(可能是用 C 编写的)执行 bash
函数。没有办法 - 一开始就用错了语言。想象一下:它必须执行 bash
才能执行 bash 函数。
警告 - 有关信号处理的详细信息可能因平台而异。
可以使 sleep
成为内置函数,请参阅 here 但这可能比它的价值更麻烦。
来自the POSIX standard for the shell:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
为了验证这一点,我们可以 运行 strace
在 shell 上执行您的脚本。从另一个终端发送 SIGINT
后,bash 只是注意到信号已被接收并且 returns 等待其子命令 sleep 命令完成:
rt_sigaction(SIGINT, {0x808a550, [], 0}, {0x80a4600, [], 0}, 8) = 0
waitpid(-1, 0xbfb56324, 0) = ? ERESTARTSYS
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = -1 EINTR (Interrupted system call)
waitpid(-1,
要使kill
具有与Ctrl-C相同的效果,您应该将SIGINT
发送到进程组。默认情况下 shell 会将新命令的每个进程放入其自己的 pgrp:
$ ps -f -o pid,ppid,pgid,tty,comm -t pts/1
PID PPID PGID TT COMMAND
3460 3447 3460 pts/1 bash
29087 3460 29087 pts/1 \_ foo.sh
29120 29087 29087 pts/1 \_ sleep
$ kill -2 -29087
现在陷阱 运行 和 shell 退出:
waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 29120
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = 29120
...
write(1, "CTRL+C pressed\n", 15) = 15
...
exit_group(0) = ?