perl信号处理仅在sighandler调用子例程时才有效

时间:2016-03-17 17:10:29

标签: linux perl signals

设定: 内核:4.1.16-v7 + 操作系统:armv7l GNU / Linux(主要是debian)

This is perl 5, version 20, subversion 2 (v5.20.2) built for arm-linux-gnueabihf-thread-multi-64int

更大的perl代码具有外部进程的一部分,有时在某个时间限制内没有响应。发生此错误时,“main”子程序通过signal_handler命令“restartservice”

重新启动

在调试此问题期间,我发现了许多关于信号处理的描述,特别是在您使用信号处理程序后重新初始化它。

http://www.perlmonks.org/?node_id=440900

qouting:

  

并非所有平台都会在信号传递后自动重新安装其(本机)信号处理程序。这意味着处理程序仅在信号发送的第一个>时间起作用。这个问题的解决方案是使用“POSIX”>信号处理程序(如果可用),它们的行为是明确定义的。

所以我试图弄清楚POSIX的做法,直到我从http://perldoc.perl.org/perlipc.html转载示例并使用我的“restartservice”子程序对其进行增强后才找到解决方案。

我的问题似乎是:当signal_handler被执行时,我无法调用已定义的子例程。

示例:

#!/usr/bin/perl
use warnings;
use strict;

sub restartservice()
{
    print "alarm reached\n";
    main();
};

sub main()
{
    while (1){
      print "while loop\n";
      eval {
        #local $SIG{ALRM} = sub { print "alarm main\n"; main();" };#fails
        local $SIG{ALRM} = sub { print "alarm main\n"; next; };#works
        #local $SIG{ALRM} = \&restartservice; #does not work ,proove below
        alarm 2;  
        sleep 5;# here i would use my normal code which sometimes hangs
        alarm 0;

      };
    };
}
main();

工作情况的输出结果:

perl perlalarm.pl 
while loop
alarm main
Exiting subroutine via next at perlalarm.pl line 17.
Exiting eval via next at perlalarm.pl line 17.
while loop
alarm main
Exiting subroutine via next at perlalarm.pl line 17.
Exiting eval via next at perlalarm.pl line 17.
while loop
alarm main
...

非工作情况的输出证明:

perl perlalarm.pl 
while loop
alarm reached
while loop
while loop
while loop

我想知道如何让子程序在该信号处理程序中工作。

2 个答案:

答案 0 :(得分:2)

在命名子例程中用main()替换next使其等同于匿名子例程,它也会开始工作。

答案 1 :(得分:2)

通常,当您在信号处理程序中时,信号会被屏蔽,除非您通过sigaction调用设置SA_NODEFER标志(在perl:POSIX :: SigAction中)。

因此,您在信号处理程序中第二次调用main()会在SIGALRM被阻止的情况下运行main()。您的执行如下所示:

time | No Signals Blocked | SIGALRM Blocked
-----+--------------------+------------------
  0  |   main()           |
  1  |    while ...       |
  2  |     eval {         |
  3  |      $SIG{ALRM}... |
 ... |     sleep          |
     |    <<ALARM>>       | $SIG{ALRM} invoked
  n  |                    | restartservice()
 n+1 |                    |  main()
 n+2 |                    |   while ...
 n+3 |                    |    ....
 n+4 |                    |  <<ALARM>>       # <-- blocked, no effect

良好的做法是在信号处理程序中执行非常小且离散的操作,例如设置标志或有时抛出异常。在perl中,eval{}+alarm成语通常是后者:

while (1) {
  my $ok = eval {
    local $SIG{ALRM} = sub { die "ALARM"; };
    alarm(5);
    do_something();
    alarm(0);
    1; # $ok
  };
  next if $ok;
  if ($@ =~ /ALARM/) {
    # timed out
  } else {
    # some other failure
  }
}