2017-05-08 91 views
4

下面的代码的行为,从终端运行当预期:的Perl未能杀死自己的pid从bash脚本运行时

perl -e 'kill -2, $$; warn HERE, $/' 

它自己发送SIGINT并达到“HERE”前去世:

~# perl -e 'kill -2, $$; warn HERE, $/' 

~# echo $? 
130 
~# 

问题:运行时,同一代码无法自行清除自动PID

~# cat 1.sh 
perl -e 'kill -2, $$; warn HERE, $/' 
~# 
~# sh 1.sh 
HERE 
~# 
~# echo $? 
0 
~# 

在另一方面,取代Perl的kill由外壳的一个工程确定:

~# cat 2.sh 
perl -e 'qx/kill -2 $$/; warn HERE, $/' 
~# 
~# sh 2.sh 
~# 
~# echo $? 
130 
~# 

没有真正理解这里发生了什么,请帮助..

+0

顺便说,'杀INT => - $$'是一个更好的写入'杀-2,$$'的方式。 – ikegami

回答

5

首先,

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的程序组。


你的故障是信号不发送到处理组的头(即,应用程序)的结果。如果检查过,报告的错误killESRCH(“没有这样的过程”)。

ESRCH pid或进程组不存在。 [...]

杀死当前进程的进程组,与

kill INT => -getpgrp() # Kill the application 

更换不当

kill INT => -$$   # XXX 

你可以让你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发送给已识别的进程而不是已识别的程序组。

+0

我很失望,你选择了“答案”,甚至没有回答你的问题(为什么两个shell的行为不同)。 – ikegami

+0

对不起,听到你很失望,不是故意的。对我来说,两个解释看起来都非常平等。我错了吗?你为什么认为挑选的人不回答我的问题? – MrCricket

+0

两种解释?我只看到一个。我们的两个答案都解释了为什么过程组工作的原因,但这不是你问为什么这两个shell的行为不同,而他们的其他答案根本就没有解释。只有我的答案(应用程序的概念)。我错过了什么? ///事实上,另一个答案声称这两个shell可以表现完全相同(通过说非流行的shell创建的流程组只有*很可能*),但这是错误的! – ikegami

0

它正常工作,以这种方式:

perl -E 'say "kill "INT", $$; warn HERE, $/'

perl -E 'say "kill 2, $$; warn HERE, $/'

kill手册页说:

负信号名称是相同的负信号数, 杀死进程组,而不是过程。例如,kill '-KILL',$ pgrp和kill -9,$ pgrp将向指定的整个 进程组发送SIGKILL。这意味着你通常想要使用正数 而不是负数信号。

+0

感谢您的回答,但是这仍然无法解释'kill -2,$$'和'qx/kill -2 $$ /' – MrCricket

+0

之间的区别,我们很确定这将取决于如何扩展$$ 。 – Sobrique

+0

'qx/kill -2 $$ /'调用OS kill cmd,但'kill -2,$$'是纯Perl代码。 – palik

4

从您的交互式终端中,perl进程杀死它所属的进程组。 (shell在自己的进程组中运行perl。)外壳报告这个不寻常的终止在$?

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

在你backticked(qx//)例如,你的交互shell启动一个新的进程组一个shell进程。 (这里并不重要,但该进程在同一个进程组中运行perl。)然后,Perl以自己的孩子身份运行系统kill命令,,其语义不同于perl kill的语义。这个grandchild命令直接发送一个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 $?