为什么这个 kill 在这个 Bash 脚本中不起作用,而只在脚本之外起作用?
Why this kill doesn't work in this Bash script but only outside of the script?
以下是一个演示效果的人为示例,必须 运行 使用 root。它在后台执行 ping 进程并尝试终止它。
#!/bin/bash
# Ensure that there is no ping process before we begin.
killall ping
sudo ping google.com > /dev/null &
PID=$!
sleep 0.5
kill $PID
echo "Exit code of kill $PID: $?"
# Check the running ping processes. There should be no ping
# process if the above `kill $PID` worked correctly.
ps aux | grep -v grep | grep ping
但是,脚本无法终止进程,即使 kill
的 return 代码为 0。以下是示例输出。
$ bash test.sh
Exit code of kill 16516: 0
root 16516 0.0 0.0 14956 2212 pts/2 S 13:22 0:00 sudo ping google.com
root 16518 1.0 0.0 13112 1292 pts/2 S 13:22 0:00 ping google.com
我注意到,如果我取出 sudo
,它就会正确地杀死它。为什么会这样?我怀疑 sudo
的子进程以某种方式搞砸了它。
更新 1:
更奇怪。如果我在脚本之后执行相同的 kill
命令,它会起作用。
$ bash test.sh
Exit code of kill 16631: 0
root 16631 3.0 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ ps aux | grep -v grep | grep ping
root 16631 0.5 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ kill 16631
$ ps aux | grep -v grep | grep ping
$
$ kill 16631
-bash: kill: (16631) - No such process
$
发生这种情况是因为控制 sudo
进程不会传播来自其自身进程组 (source) 的信号:
/*
* Do not forward signals sent by a process in the command's process
* group, as we don't want the command to indirectly kill itself.
* For example, this can happen with some versions of reboot that
* call kill(-1, SIGTERM) to kill all other processes.
*/
if (USER_SIGNALED(sc->siginfo) && sc->siginfo->si_pid != 0) {
pid_t si_pgrp = getpgid(sc->siginfo->si_pid);
if (si_pgrp != -1) {
if (si_pgrp == ec->ppgrp || si_pgrp == ec->cmnd_pid)
debug_return;
} else if (sc->siginfo->si_pid == ec->cmnd_pid) {
debug_return;
}
}
当您在脚本外部执行命令时,您 运行 它在一个单独的进程组中,因此信号被中继。
以下是一个演示效果的人为示例,必须 运行 使用 root。它在后台执行 ping 进程并尝试终止它。
#!/bin/bash
# Ensure that there is no ping process before we begin.
killall ping
sudo ping google.com > /dev/null &
PID=$!
sleep 0.5
kill $PID
echo "Exit code of kill $PID: $?"
# Check the running ping processes. There should be no ping
# process if the above `kill $PID` worked correctly.
ps aux | grep -v grep | grep ping
但是,脚本无法终止进程,即使 kill
的 return 代码为 0。以下是示例输出。
$ bash test.sh
Exit code of kill 16516: 0
root 16516 0.0 0.0 14956 2212 pts/2 S 13:22 0:00 sudo ping google.com
root 16518 1.0 0.0 13112 1292 pts/2 S 13:22 0:00 ping google.com
我注意到,如果我取出 sudo
,它就会正确地杀死它。为什么会这样?我怀疑 sudo
的子进程以某种方式搞砸了它。
更新 1:
更奇怪。如果我在脚本之后执行相同的 kill
命令,它会起作用。
$ bash test.sh
Exit code of kill 16631: 0
root 16631 3.0 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ ps aux | grep -v grep | grep ping
root 16631 0.5 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ kill 16631
$ ps aux | grep -v grep | grep ping
$
$ kill 16631
-bash: kill: (16631) - No such process
$
发生这种情况是因为控制 sudo
进程不会传播来自其自身进程组 (source) 的信号:
/*
* Do not forward signals sent by a process in the command's process
* group, as we don't want the command to indirectly kill itself.
* For example, this can happen with some versions of reboot that
* call kill(-1, SIGTERM) to kill all other processes.
*/
if (USER_SIGNALED(sc->siginfo) && sc->siginfo->si_pid != 0) {
pid_t si_pgrp = getpgid(sc->siginfo->si_pid);
if (si_pgrp != -1) {
if (si_pgrp == ec->ppgrp || si_pgrp == ec->cmnd_pid)
debug_return;
} else if (sc->siginfo->si_pid == ec->cmnd_pid) {
debug_return;
}
}
当您在脚本外部执行命令时,您 运行 它在一个单独的进程组中,因此信号被中继。