Perl 使用系统时向父级传输信号

Perl 使用系统时向父级传输信号,perl,Perl,我已经编写了一个包装器脚本,它使用system()启动另一个脚本。子脚本捕获SIGINT,并在内部处理异常。因此,当退出时,不应将SIGINT传播到其父级。。但是,在某些情况下,父级仍然会收到SIGINT。例如(parent.pl): 和child.pl: use feature qw(say); use strict; use warnings; use POSIX (); my @data = (q(child.pl 'dummy'), q(child.pl), q(bash -c chi

我已经编写了一个包装器脚本,它使用
system()
启动另一个脚本。子脚本捕获
SIGINT
,并在内部处理异常。因此,当退出时,不应将
SIGINT
传播到其父级。。但是,在某些情况下,父级仍然会收到
SIGINT
。例如(
parent.pl
):

child.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";
    }
}
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
,而在第二种和第三种情况下则不接收

这是什么原因?如何修复它,使SIGINT不会在第一种和最后一种情况下传播


(我怀疑它与Shell的类型有关,即
sh
vs
bash

首先,让我们了解执行的是什么

system($shell_command)
system($shell_command)
是的缩写

system({ "/bin/sh" } "/bin/sh", "-c", $shell_command)
my @cmd = split(' ', $shell_command);
system({ $cmd[0] } @cmd)
除非shell命令不包含shell元字符,而是包含空格,在这种情况下

是的缩写

system({ "/bin/sh" } "/bin/sh", "-c", $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被发送到会话的每个进程

正如您可以通过添加
系统(“ps-opid,ppid,pgrp,sid,cmd”)看到的那样
child.pl
,根据测试用例,会话中有三到四个进程

  • child.pl
    child.pl
    处理SIGINT。它不会被它杀死
  • 在测试用例1和4中,shell由
    parent.pl
    启动: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

为什么在第三种情况下,
system
会将
bash-c child.pl
替换为
perl child.pl
?在第四种情况下,为什么不将
sh-cchild.pl
替换为
perl child.pl
?这背后的逻辑是什么?关于“为什么在第三种情况下,
system
bash-c child.pl
替换为
perl child.pl
”,它没有这样做。Perl启动了
bash
,其中
exec
utes
child.pl
Re“在第四种情况下,为什么不将
sh-c child.pl
替换为
Perl child.pl
”,因为如果它将您要求执行的内容替换为其他内容,那将是不好的。这与在该进程中运行的初始程序(即
parent.pl
)没有关系。进程的cmd从
perl parent.pl
bash-c child.pl
(当
system
创建的子进程调用
exec
),然后到
perl child.pl
(当
bash
调用
exec
)不,bash使用exec。你知道exec不会创建进程,对吧?它更改当前进程执行的内容。