我编写了一个使用system()
启动另一个脚本的包装脚本。子脚本陷阱SIGINT
并在内部处理异常。因此,它在退出时不应将SIGINT
传播到其父级。但是,在某些情况下,父级仍然会收到SIGINT
。例如(parent.pl
):
use feature qw(say);
use strict;
use warnings;
use POSIX ();
my @data = (q(child.pl 'dummy'), q(child.pl), q(bash -c child.pl), q(sh -c child.pl));
for ( @data ) {
say "Running command '$_'..";
my $res = system $_;
my $signal = $res & 127;
my $rval = $res >> 8;
say "Parent received return value: $rval";
if ( $signal == POSIX::SIGINT ) {
say "Parent received SIGINT";
}
}
和child.pl
:
use feature qw(say);
use strict;
use warnings;
eval {
local $SIG{INT} = sub { die "Aborted by user.\n" };
sleep 10;
};
if ( $@ ) {
print "\n" . $@;
exit 0;
}
say "Timed out..";
exit 1;
如果我在超时前按 CTRL-C ,输出如下:
Running command 'child.pl 'dummy''..
^C
Aborted by user.
Parent received return value: 0
Parent received SIGINT
Running command 'child.pl'..
^C
Aborted by user.
Parent received return value: 0
Running command 'bash -c child.pl'..
^C
Aborted by user.
Parent received return value: 0
Running command 'sh -c child.pl'..
^C
Aborted by user.
Parent received return value: 0
Parent received SIGINT
因此,在第一种情况和最后一种情况下,父母会收到SIGINT
,而对于第二种和第三种情况则不会。{/ p>
这是什么原因?如何修复,以便SIGINT不会传播给第一个和最后一个案例?
(我怀疑它与Shell的类型有关,即sh
vs bash
)
答案 0 :(得分:2)
首先,让我们了解正在执行的内容。
system($shell_command)
是
的缩写system({ "/bin/sh" } "/bin/sh", "-c", $shell_command)
除非shell命令不包含shell元字符,但包含空格,在这种情况下
system($shell_command)
是
的缩写my @cmd = split(' ', $shell_command);
system({ $cmd[0] } @cmd)
因此,
system("child.pl 'dummy'") is short for system({ "/bin/sh" } "/bin/sh", "-c", "child.pl 'dummy'")
system("child.pl") is short for system({ "child.pl" } "child.pl")
system("bash -c child.pl") is short for system({ "bash" } "bash", "-c", "child.pl")
system("sh -c child.pl") is short for system({ "sh" } "sh", "-c", "child.pl")
值得注意的是,bash
替换为child.pl
,而不是在此特定情况下在单独的过程中生成它。这使得child.pl
在第三种情况下成为parent.pl
的直接子女(就像第二种情况一样)。
其次,让我们知道Ctrl-C的作用。
当按下Ctrl-C时,终端将SIGINT发送到将该终端作为其控制终端的每个进程。换句话说,SIGINT被发送到会话的每个进程。
正如您在system("ps -o pid,ppid,pgrp,sid,cmd");
添加child.pl
所看到的那样,根据测试用例,我们的会话中有三到四个进程。
child.pl
:child.pl
处理SIGINT。它并没有被它杀死。parent.pl
启动的shell:shell被SIGINT杀死。parent.pl
:system
相当于local $SIG{INT} = 'IGNORE';
,因此忽略了SIGINT。parent.pl
的登录shell:它忽略了SIGINT,但我不确定原因。我猜它是因为它是一个交互式shell。所以,这就是你所观察到的:
parent.pl
的(直接)子项为child.pl
[测试用例2和3]时,子项(child.pl
)不会因为处理SIGINT而死于SIGINT。parent.pl
的(直接)子节点是shell [测试用例1和4]时,子节点(shell)从SIGINT中消失,因为非交互式shell不会处理/忽略SIGINT。