从bash脚本运行时,Perl无法杀死自我pid

时间:2017-05-08 10:12:26

标签: perl terminal signals kill-process

以下代码在从终端运行时表现如预期:

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
~#

不太明白这里发生了什么,请帮忙..

2 个答案:

答案 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位于perlps计划群组的首位。

  • 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位于perlps计划群组的首位。

您的失败是由于未将信号发送到进程组(即应用程序)的头部而导致的。如果您选中,则报告的错误killESRCH(“没有此类流程”)。

  

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 $?