前段时间发现有个程序总是运行一段时间就挂掉,看各种日志里面都没有错误信息,感觉就是莫名其妙突然进程就没了。
大概流程是有个 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;