使用SIGINT默认处理程序

时间:2016-01-23 12:38:47

标签: bash perl signals sh

当子进程从终端收到SIGINT时,我遇到来自system()的一些奇怪的返回值。要解释一下,从Perl脚本parent.pl我使用system()运行另一个Perl脚本作为子进程,但我还需要通过shell运行子进程,所以我使用了system 'sh', '-c', ... form ..因此,孩子的父母成为sh流程,sh流程的父成为parent.pl。另外,为避免sh进程收到SIGINT信号,我将其困住了。

例如,parent.pl

use feature qw(say);
use strict;
use warnings;

for (1..3) {
    my $res = system 'sh', '-c', "trap '' INT; child$_.pl";
    say "Parent received return value: " . ($res >> 8);
}

其中child1.pl

local $SIG{INT} = "DEFAULT";
sleep 10;
say "Child timed out..";
exit 1;

child2.pl

local $SIG{INT} = sub { die };
sleep 10;
say "Child timed out..";
exit 1;

child3.pl是:

eval {
    local $SIG{INT} = sub { die };
    sleep 10;
};
if ( $@ ) {
    print $@;
    exit 2;
}
say "Child timed out..";
exit 0;

如果我运行parent.pl(从命令行)并按CTRL-C中止每个子进程,则输出为:

^CParent received return value: 130
^CDied at ./child2.pl line 7.
Parent received return value: 4
^CDied at ./child3.pl line 8.
Parent received return value: 2

现在,我想知道为什么案例1的返回值为130,案例2的返回值为4.

另外,在这种情况下确切知道"DEFAULT"信号处理程序的作用会很好。

注意:如果我将sh替换为bash(并在SIGINT中隐藏INT而不是bash,则会返回相同的值。

另见:

2 个答案:

答案 0 :(得分:6)

此问题与您之前提到的Propagation of signal to parent when using system非常相似。

来自我的bash文档:

  

当命令终止于致命信号N时,bash使用128 + N的值作为退出状态。

SIGINT通常是2,所以128 + 2给你130。

Perl的die通过检查$!$?未捕获的异常来确定其退出代码(因此,不是您使用eval的情况):

exit $! if $!;              # errno
exit $? >> 8 if $? >> 8;    # child exit status
exit 255;                   # last resort

请注意,在这种情况下,Perl以原样退出,而不是向上移位8位。

errno值恰好是4(见errno.h)。 $!变量是具有不同字符串和数值的dualvar。以数字方式使用它(如加零)得到数字侧:

use v5.10;

local $SIG{INT}=sub{
    say "numeric errno is ", $!+0;
    die
    };
sleep 10;
print q(timed out);
exit 1;

打印:

$ bash -c "perl errno.pl"
^Cnumeric errno is 4
Died at errno.pl line 6.
$ echo $?
4

答案 1 :(得分:1)

不按顺序提问:

  

此外,很高兴知道" DEFAULT"在这种情况下,信号处理程序。

将给定信号的处理程序设置为"DEFAULT",确认或恢复给定信号的默认信号处理程序,其动作取决于信号。详细信息可从the signal(7) manual page获得。 SIGINT的默认处理程序终止了该过程。

  

现在,我想知道为什么案例1的返回值为130,案例2的返回值为4.

您的child1显式设置SIGINT的默认处理程序,因此该信号会导致异常终止。 这样的过程在传统意义上没有退出代码。 shell也会收到SIGINT,但它会陷阱并忽略它。它为子进程报告的退出状态(因此也为自己报告)反映了杀死孩子的信号(数字2)。

另一方面,

另外两个子进程 catch SIGINT并正常终止响应。这些确实产生退出代码,shell传递给你(在捕获并忽略SIGINT之后)。 The documentation for die()描述了在这种情况下如何确定退出代码,但最重要的是,如果您想要使用特定代码退出,则应使用exit代替die