简体   繁体   中英

perl signal processing only works once when sighandler calls subroutine

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.

一旦在命名子例程中将main()替换为next main()使其等效于匿名子例程),它也将开始工作。

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM