perl signal processing only works once when sighandler calls subroutine

非 Y 不嫁゛ 提交于 2019-12-22 11:27:37

问题


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.


回答1:


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



回答2:


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



来源:https://stackoverflow.com/questions/36067317/perl-signal-processing-only-works-once-when-sighandler-calls-subroutine

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!