【问题标题】:Perl fails to kill self pid when running from bash script从 bash 脚本运行时,Perl 无法杀死自己的 pid
【发布时间】:2017-10-06 07:11:48
【问题描述】:

从终端运行时,以下代码的行为符合预期:

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

它发送自己SIGINT 并在到达“这里”之前死亡:

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

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

【问题讨论】:

  • 顺便说一句,kill INT => -$$ 是写kill -2, $$ 更好的方式。

标签: perl terminal signals kill-process


【解决方案1】:

首先,

 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
    

    在交互模式下,perlperlps 程序组的负责人。

  • 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
    

    在非交互模式下,shperlps 程序组的负责人。


您的失败是未将信号发送到进程组(即应用程序)的负责人的结果。如果您检查过,报告的错误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。

【讨论】:

  • 我很失望你选择的“答案”甚至没有回答你的问题(为什么两个外壳的行为不同)。
  • 很抱歉让您失望了,这不是故意的。这两种解释对我来说看起来都差不多,说真的。我错了吗?为什么你认为被选中的人没有回答我的问题?
  • 两种解释?我只看到一个。我们的两个答案都解释了进程组的工作原理,但这不是您问为什么两个外壳的行为不同的原因,而其他答案根本没有解释这一点。只有我的答案(应用程序的概念)。我错过了什么? /// 事实上,另一个答案声称两个 shell 的行为可能相同(通过说非交互式 shell 创建进程组只是可能),但这是错误的!
  • 我认为另一个答案中的 可能 词指的是 不存在的进程组,并不意味着 可能是一个新的 pr组已创建。恕我直言,这句话只是说 perl 将SIGTERM 发送到外国(可能不存在)公关组..
  • 可能确实指的是不存在。如果它可能不存在,那意味着它可能没有被创建,这意味着它有可能被创建,但这是完全错误的。非交互式 shell 不会创建进程组,正如我所解释的,其原因是因为 shell 将 perl 视为同一应用程序的一部分。
【解决方案2】:

从您的交互式终端,perl 进程会杀死它所属的进程组。 (shell 在其自己的进程组中运行 perl。)shell 在 $? 中报告此异常终止:

t0 交互式 shell (pid=123, pgrp=123) | t1 +------> perl -e (pid=456, pgrp=456, parent=123) | | t2 (wait) kill(-2, 456) (在 perl 中,与 kill pgrp 456 w/SIGINT 相同) | | t3(等待)*SIGINT* | t4 报告 $?

从您的 shell 脚本 中,perl 进程会杀死(可能)不存在的进程组,然后成功退出。您的交互式 shell 创建了一个新进程组来运行您的 shell 脚本,然后该脚本作为同一进程组中的子进程运行 perl。

t0 外壳 (pid=123, pgrp=123) | t1 +-------> shell:1.sh (pid=456, pgrp=456, parent=123) | | t2 (等待) +-------------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (wait) (wait) kill pgrp 789 with SIGINT (error: no such pgrp) | | | t4 (wait) (等待) 退出成功 | | t5(等待)退出成功 | t6 报告 $?

在您的反引号 (qx//) 示例中,您的交互式 shell 使用新进程组启动一个 shell 进程。 (在这里并不重要,但该进程在其同一进程组中运行 perl。)然后 Perl 作为自己的子进程运行系统 kill 命令,其语义不同于 perl kill。这个孙子命令直接向 perl PID 发送 SIGINT,而不是向进程组发送 SIGINT。 Perl 终止,并且该退出代码作为脚本的退出代码传送,因为它是脚本中的最后一个命令。

这张图比上一张有点忙:

t0 外壳 (pid=123, pgrp=123) | t1 +-------> shell:2.sh (pid=456, pgrp=456, parent=123) | | t2 (等待) +----------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (等待) (等待) +---------> /bin/kill SIGINT 789 | | | | t4 (wait) (等待) *SIGINT* 退出成功 | | t5(等待)返回 $? | t6 报告 $?

【讨论】:

    【解决方案3】:

    这样可以正常工作:

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

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

    kill 手册页说:

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

    【讨论】:

    • 感谢您的回答,但这仍然不能解释 kill -2, $$qx/kill -2 $$/ 之间的区别
    • 很确定这将取决于 $$ 的扩展方式。
    • qx/kill -2 $$/ 调用 OS kill cmd,但 kill -2, $$ 是纯 perl 代码。
    • 当然,但是为什么 perl kill 杀死进程组不能作为操作系统杀死?
    猜你喜欢
    • 1970-01-01
    • 2016-10-08
    • 2017-08-04
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多