perl signal processing only works once when sighan

2019-05-26 15:09发布

Setup: kernel: 4.1.16-v7+ OS: armv7l GNU/Linux (mostly debian)

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

A larger perl code has a part of external process which sometimes does not respond within a certain timelimit. When this error occurs the "main" subroutine gets restarted via signal_handler command "restartservice"

During debugging this issue i found numerous descriptions about signal handling and especially reinitialising a signal handler once you have used it.

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

qouting:

Not all platforms automatically reinstall their (native) signal handlers >after a signal delivery. This means that the handler works only the first >time the signal is sent. The solution to this problem is to use "POSIX" >signal handlers if available, their behaviour is well-defined.

So i tried to figure out the POSIX way of doing it, did not found a solution until i reproduced the example from http://perldoc.perl.org/perlipc.html and enhanced it with my "restartservice" subroutine.

My issue seems to be: I cannot call an already defined subroutine when a signal_handler gets executed.

Example:

#!/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();

Output proove of working situation:

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
...

Output proove of not working situation:

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

I would like to know what i have to do to get a subroutine working in that signal handler.

2条回答
萌系小妹纸
2楼-- · 2019-05-26 15:52

Once you replace main() with next in the named subroutine to make it equivalent to the anonymous one, it starts working, too.

查看更多
相关推荐>>
3楼-- · 2019-05-26 16:09

Generally, signals are masked when you are inside a signal handler, unless you set the SA_NODEFER flag through a sigaction call (in perl: POSIX::SigAction).

So, your second invocation of main() from within the signal handler runs main() with SIGALRM blocked. Your execution looks like this:

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

Good practice is to do something very small and discrete in a signal handler, like set a flag or, sometimes, throw an exception. In perl, the eval{}+alarm idiom is usually the latter:

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
  }
}
查看更多
登录 后发表回答