前段时间发现有个程序总是运行一段时间就挂掉,看各种日志里面都没有错误信息,感觉就是莫名其妙突然进程就没了。



大概流程是有个 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;