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,而对于第二种和第三种情况,它不接收SIGINT.
这是什么原因?如何修复以使SIGINT不会传播给第一个和最后一个案例?
(我怀疑它与Shell的类型有关,即sh vs bash)
解决方法
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","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","child.pl") system("sh -c child.pl") is short for system({ "sh" } "sh","child.pl")
值得注意的是,bash将自己替换为child.pl,而不是在此特定情况下在单独的进程中生成它.这使得child.pl在第三种情况下成为parent.pl的直接子项(就像第二种情况一样).
其次,让我们知道Ctrl-C的作用.
按下Ctrl-C时,终端将SIGINT发送到以该终端为控制终端的每个进程.换句话说,SIGINT被发送到会话的每个进程.
通过添加系统可以看到(“ps -o pid,ppid,pgrp,sid,cmd”);到child.pl,我们的会话中有三到四个进程,具体取决于测试用例.
> child.pl:child.pl处理SIGINT.它没有被它杀死.
> shell在测试用例1和4中由parent.pl启动:shell被SIGINT杀死.
> parent.pl:系统相当于本地$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.