以下代码在从终端运行时表现如预期:
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
~#
不太明白这里发生了什么,请帮忙..
答案 0 :(得分:5)
首先,
kill -2, $$
最好写成
kill 2, -$$
更好的选择是
kill INT => -$$
这些将SIGINT
发送到指定的进程组。
您的主要问题似乎是两个炮弹表现不同的原因。本节解释了这一点。
进程组代表一个应用程序。
当您从交互式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
(“没有此类流程”)。
ESRCH
pid或进程组不存在。 [...]
要终止当前进程的进程组,请替换不正确的
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发送到已识别的进程而不是已识别的程序组。
答案 1 :(得分:4)
通过交互式终端,perl进程会终止其所属的进程组。 (shell在其自己的进程组中运行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脚本,然后该脚本在同一进程组中作为子进程运行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进程。 (这并不重要,但是该进程在同一个进程组中运行perl。)然后Perl作为自己的子进程运行系统kill
命令,的语义与perl {{的语义不同。 1}} 的。这个孙子命令直接向perl PID发送SIGINT,而不是向进程组发送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 $?