前段时间发现有个程序总是运行一段时间就挂掉,看各种日志里面都没有错误信息,感觉就是莫名其妙突然进程就没了。
大概流程是有个 perl 程序 a.pl
.....
my $pid = fork();
if ( !$pid ) {
my $cmdRet = `b.pl 2>&1`;
print FILE $cmdRet;
if ( $status ) {
warn "task failed";
} else {
warn "task success";
}
exit;
}
waitpid ........
b.pl 里面会执行 rsync 去获取一些文件,他会循环到几个机器上面去 rsync
for ( @hosts ) {
my $result = `rsync xxxxx 2>&1`;
if ( $? ) {
log($result);
log("failed");
} else {
log($result);
log("success");
}
}
sub log {
my $msg = shift;
print $msg;
# 然后通过 IO::Socket::INET 发送给另外一个 server a
}
现象是,时不时的, b.pl 会只 rsync 了某几台(不确定是几台)机器上面的文件,然后就不继续了,从 server a 上面能收到他发日志,最后一条是 success 的信息
从 a.pl 记录的日志那里看, FILE 里面记录的内容丢失了 server a 收到的最后一部分的数据,多少数据不一定,不过肯定是没有那个 success 信息。 这个文件里面也没有任何的错误信息。
程序代码啥的都不动,rsync 的文件数不是总是一样的,也有文件多的时候没出错的时候,同时也设置了打开文件数为 65536.
后来发现问题就在 b.pl 里面的 log 里面。因为要发送到其他机器,怕挂住影响后续程序,所以设置了一个 alarm。
eval {
alarm 5;
xxxxxxx;
alarm 0;
};
if ( $@ ) {
print "error when send";
}
这个 alarm 没有设置 handle 的函数,这样就会导致 alarm 到期的时候,会直接让整个 perl 程序挂掉,并显示 "Alarm clock",而且这个输出不在标准错误和标准输出里面。
修复也简单,alarm 前设置一个 handler 就好了。
另外,还有个问题,一个 alarm 会中断前一个 alarm,所以类似 sleep 的使用,可以这样
my $previousAlarm = 0;
eval {
local $SIG{ALRM} = sub { die 'alarm'; };
$previousAlarm = alarm 5;
xxxx
alarm 0;
};
alarm 0;
if ( $@ ) {
xxxxx;
}
alarm $previousAlarm;