当来自 bash 脚本的 运行 时,Perl 无法终止自身 pid
Perl fails to kill self pid when running from bash script
当 运行 来自终端 时,以下代码的行为符合预期:
perl -e 'kill -2, $$; warn HERE, $/'
它发送自己 SIGINT
并在到达 "HERE" 之前死亡:
~# perl -e 'kill -2, $$; warn HERE, $/'
~# echo $?
130
~#
问题:当 运行 来自 shell 脚本 :
时,相同的代码无法杀死自身 PID
~# cat 1.sh
perl -e 'kill -2, $$; warn HERE, $/'
~#
~# sh 1.sh
HERE
~#
~# echo $?
0
~#
另一方面,用 shell 替换 perl 的 kill
可以正常工作:
~# cat 2.sh
perl -e 'qx/kill -2 $$/; warn HERE, $/'
~#
~# sh 2.sh
~#
~# echo $?
130
~#
不太明白这里发生了什么,请帮忙..
这种方式效果很好:
perl -E 'say "kill "INT", $$; warn HERE, $/'
perl -E 'say "kill 2, $$; warn HERE, $/'
kill 手册页说:
A negative signal name is the same as a negative signal number,
killing process groups instead of processes. For example, kill
'-KILL', $pgrp and kill -9, $pgrp will send SIGKILL to the entire
process group specified. That means you usually want to use positive
not negative signals.
首先,
kill -2, $$
最好写成
kill 2, -$$
更好的选择是
kill INT => -$$
这些发送SIGINT
到指定的进程组。
您的主要问题似乎是为什么两个 shell 的行为不同。本节对此进行了解释。
进程组代表一个应用程序。
当您从交互式 shell 启动程序时,它不是较大应用程序的一部分,因此 shell 会为该程序创建一个新的进程组。
但是,由脚本(即非交互式 shell)创建的进程与脚本本身属于同一应用程序的一部分,因此 shell 不会创建新的进程组为了他们。
您可以使用以下方法将其可视化:
sh -i <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
输出如下:
$ perl -e 'system ps => -o => "pid,ppid,pgrp,comm"'
PID PPID PGRP COMMAND
8179 8171 8179 bash
14654 8179 14654 sh
14655 14654 14655 perl
14656 14655 14655 ps
$ exit
在交互模式下,perl
在perl
和ps
的程序组中。
sh <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
输出如下:
PID PPID PGRP COMMAND
8179 8171 8179 bash
14584 8179 14584 sh
14585 14584 14584 perl
14586 14585 14584 ps
在非交互模式下,sh
在perl
和ps
的程序组中。
你的失败是因为没有将信号发送到进程组的头(即应用程序)。如果您检查过,报告的错误 kill
是 ESRCH
("No such process").
ESRCH
The pid or process group does not exist. [...]
杀掉当前进程的进程组,替换不合适的
kill INT => -$$ # XXX
和
kill INT => -getpgrp() # Kill the application
您可以通过简单地调用以下命令使您的 perl
成为它自己的进程组的组长:
setpgrp();
测试:
$ sh <<< 'perl -e '\''system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16325 8179 16325 sh
16326 16325 16325 perl
16327 16326 16325 ps
$ sh <<< 'perl -e '\''setpgrp(); system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16349 8179 16349 sh
16350 16349 16350 perl
16351 16350 16350 ps
这不是你通常想做的事情。
最后是 Perl 代码
kill INT => -$pgrp
等效于 kill
命令行实用程序的以下调用:
kill -s INT -$pgrp
kill -INT -$pgrp
kill -2 -$pgrp
您的 qx//
程序中缺少 -
,因此它将 SIGINT 发送到已识别的进程而不是已识别的程序组。
从您的交互式终端,perl 进程终止了它所属的进程组。 (shell 运行s perl 在其自己的进程组中。)shell 在 $?
中报告此异常终止:
t0 interactive shell (pid=123, pgrp=123)
|
t1 +------> perl -e (pid=456, pgrp=456, parent=123)
| |
t2 (wait) kill(-2, 456) (in perl, same as kill pgrp 456 w/ SIGINT)
| |
t3 (wait) *SIGINT*
|
t4 report $?
从您的shell 脚本,perl 进程杀死了一个(可能)不存在的进程组,然后成功退出。您的交互式 shell 创建了一个新进程组,其中 运行 您的 shell 脚本,然后该脚本 运行s perl 作为同一进程组中的子进程。
t0 shell (pid=123, pgrp=123)
|
t1 +-------> shell:1.sh (pid=456, pgrp=456, parent=123)
| |
t2 (wait) +-------------> perl -e (pid=789, pgrp=456, parent=456)
| | |
t3 (wait) (wait) kill pgrp 789 with SIGINT (error: no such pgrp)
| | |
t4 (wait) (wait) exit success
| |
t5 (wait) exit success
|
t6 report $?
在您的 反引号 (qx//
) 示例 中,您的交互式 shell 启动了一个带有新进程组的 shell 进程。 (这里并不重要,但是进程 运行s perl 在其同一个进程组中。)然后 Perl 运行s 作为它自己的子系统 kill
命令,其语义不同于 perl kill
。这个孙子命令直接将 SIGINT 发送到 perl PID,而不是将 SIGINT 发送到进程组。 Perl 终止,并且该退出代码作为脚本的退出代码传送,因为它是脚本中的最后一个命令。
这张图比上一张要忙一些:
t0 shell (pid=123, pgrp=123)
|
t1 +-------> shell:2.sh (pid=456, pgrp=456, parent=123)
| |
t2 (wait) +----------> perl -e (pid=789, pgrp=456, parent=456)
| | |
t3 (wait) (wait) +---------> /bin/kill SIGINT 789
| | | |
t4 (wait) (wait) *SIGINT* exit success
| |
t5 (wait) return $?
|
t6 report $?
当 运行 来自终端 时,以下代码的行为符合预期:
perl -e 'kill -2, $$; warn HERE, $/'
它发送自己 SIGINT
并在到达 "HERE" 之前死亡:
~# perl -e 'kill -2, $$; warn HERE, $/'
~# echo $?
130
~#
问题:当 运行 来自 shell 脚本 :
时,相同的代码无法杀死自身 PID~# cat 1.sh
perl -e 'kill -2, $$; warn HERE, $/'
~#
~# sh 1.sh
HERE
~#
~# echo $?
0
~#
另一方面,用 shell 替换 perl 的 kill
可以正常工作:
~# cat 2.sh
perl -e 'qx/kill -2 $$/; warn HERE, $/'
~#
~# sh 2.sh
~#
~# echo $?
130
~#
不太明白这里发生了什么,请帮忙..
这种方式效果很好:
perl -E 'say "kill "INT", $$; warn HERE, $/'
perl -E 'say "kill 2, $$; warn HERE, $/'
kill 手册页说:
A negative signal name is the same as a negative signal number, killing process groups instead of processes. For example, kill '-KILL', $pgrp and kill -9, $pgrp will send SIGKILL to the entire process group specified. That means you usually want to use positive not negative signals.
首先,
kill -2, $$
最好写成
kill 2, -$$
更好的选择是
kill INT => -$$
这些发送SIGINT
到指定的进程组。
您的主要问题似乎是为什么两个 shell 的行为不同。本节对此进行了解释。
进程组代表一个应用程序。
当您从交互式 shell 启动程序时,它不是较大应用程序的一部分,因此 shell 会为该程序创建一个新的进程组。
但是,由脚本(即非交互式 shell)创建的进程与脚本本身属于同一应用程序的一部分,因此 shell 不会创建新的进程组为了他们。
您可以使用以下方法将其可视化:
sh -i <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
输出如下:$ perl -e 'system ps => -o => "pid,ppid,pgrp,comm"' PID PPID PGRP COMMAND 8179 8171 8179 bash 14654 8179 14654 sh 14655 14654 14655 perl 14656 14655 14655 ps $ exit
在交互模式下,
perl
在perl
和ps
的程序组中。sh <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
输出如下:PID PPID PGRP COMMAND 8179 8171 8179 bash 14584 8179 14584 sh 14585 14584 14584 perl 14586 14585 14584 ps
在非交互模式下,
sh
在perl
和ps
的程序组中。
你的失败是因为没有将信号发送到进程组的头(即应用程序)。如果您检查过,报告的错误 kill
是 ESRCH
("No such process").
ESRCH
The pid or process group does not exist. [...]
杀掉当前进程的进程组,替换不合适的
kill INT => -$$ # XXX
和
kill INT => -getpgrp() # Kill the application
您可以通过简单地调用以下命令使您的 perl
成为它自己的进程组的组长:
setpgrp();
测试:
$ sh <<< 'perl -e '\''system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16325 8179 16325 sh
16326 16325 16325 perl
16327 16326 16325 ps
$ sh <<< 'perl -e '\''setpgrp(); system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16349 8179 16349 sh
16350 16349 16350 perl
16351 16350 16350 ps
这不是你通常想做的事情。
最后是 Perl 代码
kill INT => -$pgrp
等效于 kill
命令行实用程序的以下调用:
kill -s INT -$pgrp
kill -INT -$pgrp
kill -2 -$pgrp
您的 qx//
程序中缺少 -
,因此它将 SIGINT 发送到已识别的进程而不是已识别的程序组。
从您的交互式终端,perl 进程终止了它所属的进程组。 (shell 运行s perl 在其自己的进程组中。)shell 在 $?
中报告此异常终止:
t0 interactive shell (pid=123, pgrp=123) | t1 +------> perl -e (pid=456, pgrp=456, parent=123) | | t2 (wait) kill(-2, 456) (in perl, same as kill pgrp 456 w/ SIGINT) | | t3 (wait) *SIGINT* | t4 report $?
从您的shell 脚本,perl 进程杀死了一个(可能)不存在的进程组,然后成功退出。您的交互式 shell 创建了一个新进程组,其中 运行 您的 shell 脚本,然后该脚本 运行s perl 作为同一进程组中的子进程。
t0 shell (pid=123, pgrp=123) | t1 +-------> shell:1.sh (pid=456, pgrp=456, parent=123) | | t2 (wait) +-------------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (wait) (wait) kill pgrp 789 with SIGINT (error: no such pgrp) | | | t4 (wait) (wait) exit success | | t5 (wait) exit success | t6 report $?
在您的 反引号 (qx//
) 示例 中,您的交互式 shell 启动了一个带有新进程组的 shell 进程。 (这里并不重要,但是进程 运行s perl 在其同一个进程组中。)然后 Perl 运行s 作为它自己的子系统 kill
命令,其语义不同于 perl kill
。这个孙子命令直接将 SIGINT 发送到 perl PID,而不是将 SIGINT 发送到进程组。 Perl 终止,并且该退出代码作为脚本的退出代码传送,因为它是脚本中的最后一个命令。
这张图比上一张要忙一些:
t0 shell (pid=123, pgrp=123) | t1 +-------> shell:2.sh (pid=456, pgrp=456, parent=123) | | t2 (wait) +----------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (wait) (wait) +---------> /bin/kill SIGINT 789 | | | | t4 (wait) (wait) *SIGINT* exit success | | t5 (wait) return $? | t6 report $?