Perl 处理简单父子IPC的信号和子出口值组合?
我试图找出正确的方法来处理一个简单的父子进程间通信(IPC)案例。子对象通过子对象的Perl 处理简单父子IPC的信号和子出口值组合?,perl,Perl,我试图找出正确的方法来处理一个简单的父子进程间通信(IPC)案例。子对象通过子对象的STDOUT句柄向父对象发送消息。父代不会向子代发送任何消息(如果子代死亡,则SIGPIPE除外)。此外,子级和父级都需要处理来自终端用户的SIGINT信号。父进程的主要困难是,当子进程死于SIGINT或SIGPIPE时,要正确获取子进程的退出状态 parent.pl: #! /usr/bin/env perl use feature qw(say); use strict; use warnings; my
STDOUT
句柄向父对象发送消息。父代不会向子代发送任何消息(如果子代死亡,则SIGPIPE
除外)。此外,子级和父级都需要处理来自终端用户的SIGINT
信号。父进程的主要困难是,当子进程死于SIGINT
或SIGPIPE
时,要正确获取子进程的退出状态
parent.pl:
#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
my $child_pid = open ( my $fh, '-|', 'child.pl' ) or die "Could not start child: $!";
$SIG{INT} = sub {
$SIG{CHLD}="IGNORE";
die "Caught SIGINT"
};
my $child_error;
$SIG{CHLD} = sub {
$SIG{INT}="IGNORE";
waitpid $child_pid, 0;
$child_error = $?;
die "Caught SIGCHLD: Child exited.."
};
eval {
while (1) {
msg( "sleeping(1).." );
sleep 1;
#internal_failure();
msg( "waiting for child input.." );
my $line = <$fh>;
if ( defined $line ) {
chomp $line;
msg( "got line: '$line'" );
}
else {
die "Could not read child pipe.";
}
msg( "sleeping(2).." );
sleep 2;
}
};
if ( $@ ) {
chomp $@;
msg( "interrupted: '$@'" );
}
my $close_ok = close $fh; # close() will implicitly call waitpid()
if ( !$close_ok ) {
msg( "Closing child pipe failed: $!" );
if ( !defined $child_error ) {
waitpid $child_pid, 0;
}
}
if ( !defined $child_error ) {
$child_error = $?;
}
my $child_signal = $child_error & 0x7F;
if ( $child_signal ) {
msg( "Child died from signal: $child_signal" );
}
else {
msg( "Child exited with return value: " . ($child_error >> 8) );
}
exit;
sub msg { say "Parent: " . $_[0] }
sub internal_failure {
$SIG{CHLD}="IGNORE";
$SIG{INT}="IGNORE";
die "internal failure";
}
#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
$SIG{PIPE} = sub {
$SIG{INT}="IGNORE";
die "Caught SIGPIPE: Parent died.";
};
$SIG{INT} = sub {
$SIG{PIPE}="IGNORE";
die "Caught SIGINT\n"; # For some reason a newline is needed here !?
};
#local $SIG{INT} = "IGNORE";
STDOUT->autoflush(1); # make parent see my messages immediately
msg( "running.." );
eval {
sleep 2;
say "Hello"; # should trigger SIGPIPE signal if parent is dead
sleep 1;
};
if ( $@ ) {
chomp $@;
msg( "interrupted: '$@'" );
exit 2;
}
msg( "exits" );
exit 1;
从命令行运行parent.pl
的正常输出为:
Parent: sleeping(1)..
Child: running..
Parent: waiting for child input..
Parent: got line: 'Hello'
Parent: sleeping(2)..
Child: exits
Parent: interrupted: 'Caught SIGCHLD: Child exited.. at ./parent.pl line 20, <$fh> line 1.'
Parent: Closing child pipe failed: No child processes
Parent: Child exited with return value: 1
避免在以后接收到SIGCHLD
。例如,如果我没有禁用子信号,它可能会到达父对象中的清理部分(在eval
块之后),并使父对象在完成清理之前死亡
问题2:处理信号
如果在启动父级后按CTRL-C,输出通常如下所示:
Parent: sleeping(1)..
Child: running..
Parent: waiting for child input..
^CChild: interrupted: 'Caught SIGINT'
Parent: interrupted: 'Caught SIGINT at ./parent.pl line 11.'
Parent: Closing child pipe failed: No child processes
Parent: Child died from signal: 127
这里的问题是孩子的退出状态。它应该是2,但它被信号127杀死。这里的信号127是什么意思
问题3:母公司因内部故障死亡
如果我取消对该行的注释
#internal_failure();
在parent.pl
中,输出为:
Parent: sleeping(1)..
Child: running..
Parent: interrupted: 'internal failure at ./parent.pl line 71.'
Child: interrupted: 'Caught SIGPIPE: Parent died. at ./child.pl line 9.'
Parent: Closing child pipe failed: No child processes
Parent: Child died from signal: 127
除了子进程的退出状态外,这似乎工作得很好。它应该是2,而是被信号127杀死。您将子项设置为(
$SIG{CHLD}=“IGNORE”
),然后您调用了waitpid
,不是一次,而是两次
对waitpid
的后续调用将$?
设置为-1
(发出错误信号,但您将其误解为“被信号杀死”),并且$代码>到无子进程
修正:
$ diff -u ./parent.pl{~,}
--- ./parent.pl~ 2016-09-19 19:28:39.778244653 -0700
+++ ./parent.pl 2016-09-19 19:28:10.698227008 -0700
@@ -7,16 +7,12 @@
my $child_pid = open ( my $fh, '-|', 'child.pl' ) or die "Could not start child: $!";
$SIG{INT} = sub {
- $SIG{CHLD}="IGNORE";
+ $SIG{CHLD}="DEFAULT";
die "Caught SIGINT"
};
-my $child_error;
-
$SIG{CHLD} = sub {
$SIG{INT}="IGNORE";
- waitpid $child_pid, 0;
- $child_error = $?;
die "Caught SIGCHLD: Child exited.."
};
@@ -44,29 +40,19 @@
msg( "interrupted: '$@'" );
}
-my $close_ok = close $fh; # close() will implicitly call waitpid()
-if ( !$close_ok ) {
- msg( "Closing child pipe failed: $!" );
- if ( !defined $child_error ) {
- waitpid $child_pid, 0;
- }
-}
-if ( !defined $child_error ) {
- $child_error = $?;
-}
-my $child_signal = $child_error & 0x7F;
-if ( $child_signal ) {
- msg( "Child died from signal: $child_signal" );
-}
-else {
- msg( "Child exited with return value: " . ($child_error >> 8) );
-}
+close $fh; # close() will implicitly call waitpid()
+
+if ( $? == -1 ) { msg( "Closing child pipe failed: $!" ); }
+elsif ( $? & 0x7F ) { msg( "Child died from signal ".( $? & 0x7F ) ); }
+elsif ( $? >> 8 ) { msg( "Child exited with error ".( $? >> 8 ) ); }
+else { msg( "Child executed successfully" ); }
+
exit;
sub msg { say "Parent: " . $_[0] }
sub internal_failure {
- $SIG{CHLD}="IGNORE";
+ $SIG{CHLD}="DEFAULT";
$SIG{INT}="IGNORE";
die "internal failure";
}
修复了父.pl
:
#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
my $child_pid = open ( my $fh, '-|', 'child.pl' ) or die "Could not start child: $!";
$SIG{INT} = sub {
$SIG{CHLD}="DEFAULT";
die "Caught SIGINT"
};
$SIG{CHLD} = sub {
$SIG{INT}="IGNORE";
die "Caught SIGCHLD: Child exited.."
};
eval {
while (1) {
msg( "sleeping(1).." );
sleep 1;
#internal_failure();
msg( "waiting for child input.." );
my $line = <$fh>;
if ( defined $line ) {
chomp $line;
msg( "got line: '$line'" );
}
else {
die "Could not read child pipe.";
}
msg( "sleeping(2).." );
sleep 2;
}
};
if ( $@ ) {
chomp $@;
msg( "interrupted: '$@'" );
}
close $fh; # close() will implicitly call waitpid()
if ( $? == -1 ) { msg( "Closing child pipe failed: $!" ); }
elsif ( $? & 0x7F ) { msg( "Child died from signal ".( $? & 0x7F ) ); }
elsif ( $? >> 8 ) { msg( "Child exited with error ".( $? >> 8 ) ); }
else { msg( "Child executed successfully" ); }
exit;
sub msg { say "Parent: " . $_[0] }
sub internal_failure {
$SIG{CHLD}="DEFAULT";
$SIG{INT}="IGNORE";
die "internal failure";
}
#/usr/bin/env perl
使用特征qw(例如);
严格使用;
使用警告;
我的$child_pid=open(我的$fh、-|'、'child.pl')或die“无法启动child:$!”;
$SIG{INT}=sub{
$SIG{CHLD}=“默认”;
死亡“抓住信号”
};
$SIG{CHLD}=sub{
$SIG{INT}=“忽略”;
死亡“捕获信号:孩子退出…”
};
评估{
而(1){
味精(“睡眠(1)…);
睡眠1;
#内部故障();
msg(“等待子输入…”);
我的$line=;
如果(定义的$行){
chomp$行;
msg(“获取行:“$line”);
}
否则{
“无法读取子管道。”;
}
味精(“睡眠(2)…);
睡眠2;
}
};
如果($@){
chomp$@;
msg(“中断:“$@”);
}
收盘价$fh;#close()将隐式调用waitpid()
如果($?==-1){msg(“关闭子管道失败:$!”);}
elsif($?&0x7F){msg(“孩子死于信号”。($?&0x7F));}
elsif($?>>8){msg(“子项因错误退出”。($?>>8));}
else{msg(“成功执行子项”);}
出口
子消息{say“Parent:.$\u0]}
次内部故障{
$SIG{CHLD}=“默认”;
$SIG{INT}=“忽略”;
模具“内部失效”;
}
信号处理仍然很混乱,但我想避免更改与修复无关的代码。
$child\u error
可能是由于在信号处理程序之外使用waitpid
而导致的-1
。(-1&0x7F==0x7F==127
)。关闭句柄也会调用waitpid
。你弄得一团糟。选择要调用waitpid的位置,并消除另外两个代码>到处都是!哇!为什么?注意:一般来说,您需要在SIGCHLD处理程序中有一个循环,因为可能有多个孩子死亡。@ikegami感谢您的评论!关于SigChLD的循环:我在考虑这个问题,但是因为我只考虑了一个孩子,我认为这不是必要的,就像我说的,“一般来说”。这里,就像你说的,没问题。谢谢你的清理,看起来不错!我看到从子信号处理程序内部删除waitpid
似乎是可行的,但是根据:“如果您为SIGCHLD安装了信号处理程序,那么$?的值在该处理程序外部通常是错误的。”。那么如何从文档中解释这个语句呢?也就是说$?如果在处理程序中设置,但在处理程序外部检查,则可能不正确。但是在本例中,您在处理程序外部调用waitpid,因此当然可以检查$?这里。@zdim“当我交换它们时,它表现得更好。”我不确定我是否理解。这里的交换是什么意思?eval
中的local
处理程序?设置一个标志而不是死亡?@HåkonHægland我为我对涉及其他信号的处理程序的无知评论道歉——你说得很好,我没看到。我确实认为这不必要地使事情复杂化,但这些评论是不恰当的,因为这确实是有意的。我把它们拿走了。很抱歉。
#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
my $child_pid = open ( my $fh, '-|', 'child.pl' ) or die "Could not start child: $!";
$SIG{INT} = sub {
$SIG{CHLD}="DEFAULT";
die "Caught SIGINT"
};
$SIG{CHLD} = sub {
$SIG{INT}="IGNORE";
die "Caught SIGCHLD: Child exited.."
};
eval {
while (1) {
msg( "sleeping(1).." );
sleep 1;
#internal_failure();
msg( "waiting for child input.." );
my $line = <$fh>;
if ( defined $line ) {
chomp $line;
msg( "got line: '$line'" );
}
else {
die "Could not read child pipe.";
}
msg( "sleeping(2).." );
sleep 2;
}
};
if ( $@ ) {
chomp $@;
msg( "interrupted: '$@'" );
}
close $fh; # close() will implicitly call waitpid()
if ( $? == -1 ) { msg( "Closing child pipe failed: $!" ); }
elsif ( $? & 0x7F ) { msg( "Child died from signal ".( $? & 0x7F ) ); }
elsif ( $? >> 8 ) { msg( "Child exited with error ".( $? >> 8 ) ); }
else { msg( "Child executed successfully" ); }
exit;
sub msg { say "Parent: " . $_[0] }
sub internal_failure {
$SIG{CHLD}="DEFAULT";
$SIG{INT}="IGNORE";
die "internal failure";
}