在实际的编程中,启动一个子进程,然后等待其结束,然后父进程接着干别的,这是很理想的情况。经常地,我们会需要一个Timeout的机制,在子进程运行的时间超出了我们的期望,明显情况不对的时候能够及时地终止它。
好消息是Perl提供了类似于Unix的信号机制,我们可以在某个信号上注册一个对应该信号的处理逻辑,比如收到ALARM信号的时候就强制退出(杀死)子进程。而父进程通过调用alarm函数使得在指定的时间会有一个SIGALRM信号发送过来,这样就能够通过(控制发送ALARM信号+ALARM信号处理函数)实现一个定时的机制。
当然,父进程还是通过waitpid以等待子进程的正常结束,ALARM信号只是用于处理Timeout的情况。所以waitpid之后,要调用“alarm 0”来取消之前的Timeout定时器。
一个典型的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| use strict; use POSIX ":sys_wait_h"; my $timeOut = 5; my $cmd = 'ping 10.10.10.10'; my $rv = 0; my $child_pid = open ( my $CMD, "-|", $cmd ) // return -1; if ( $child_pid ) { eval { local $SIG{ALRM} = sub { kill 'SIGTERM', $child_pid; sleep 1; kill 'SIGKILL', $child_pid; die "timeout\n"; }; alarm $timeOut if $timeOut; waitpid($child_pid, 0); $rv = $?; alarm 0 if $timeOut; }; if ( $@ ) { print "Error: $@\n"; $rv = -1; } if ($rv != 0) { print "Failed to issue the command:" . "$rv" . "\n"; } else { print "Succeed to issue the command:" . "$rv" . "\n"; } } close $CMD;
|
上面的代码通过一个ping命令来模拟一个运行超时的命令。第10行的open函数用于异步启动一个子进程执行该命令。16~21行的代码实现了针对SIGALRM信号的处理,24行启动了Alarm机制的timer,28行在非timeout的情况(子进程正常退出的情况)下取消timer。
注意eval经常被用于这种情况去捕获一个代码block中可能的异常。