【问题标题】:Why doesn't die in alarm signal handler kill the process?为什么在警报信号处理程序中不会死掉进程?
【发布时间】:2012-04-29 14:00:57
【问题描述】:

来自如何为 Perl 系统调用指定超时限制?

eval { 
    local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required 
    alarm $timeout; 
    $nread = sysread SOCKET, $buffer, $size; 
    alarm 0; 
}; 
if ($@) { 
    die unless $@ eq "alarm\n";   # propagate unexpected errors 
    # timed out 
} 
else { 
    # didn't 
} 

如果发生超时,sub { die "alarm\n" }; 是否应该导致进程结束。我想我无法理解die。这个http://www.cs.cf.ac.uk/Dave/PERL/node111.html 说“die() 函数用于退出您的脚本并显示一条消息供用户阅读”。但是,在上述脚本的情况下,该脚本将处理#timed out 中的代码。 sysread 也继续工作。我有一个休眠 30 秒的 perl 脚本,而不是 sysread。我的超时设置为 10 秒。正如预期的那样,#timed out 中的代码已执行,但脚本继续休眠。感谢任何输入

【问题讨论】:

    标签: perl die


    【解决方案1】:

    die 不会导致进程结束,它会引发异常。

    现在,如果没有捕获到异常,则结束一个进程,但是您有 code 来捕获这个异常。

    该过程不会结束,因为您明确阻止它结束。


    由于您不太清楚自己得到了什么行为,可能还有另一种可能性:您使用的是 Windows 版本的 Perl。

    alarm 是一个 Unix 系统调用。这是非常有目的的(在经过一定时间后发送信号)在 Windows 上没有意义,因为 Windows 没有信号。

    Perl 在某种程度上模拟了alarm,但只是以非常有限的方式。 sleep 很可能是唯一可以被alarm 中断的操作。否则,只在语句之间检查超时。

    所以它不会中断sysread,但是一旦sysread 返回,Perl 就会注意到超时时间已过,然后模拟一个信号。

    【讨论】:

    • @doon,添加了替代答案。
    • 谢谢。我正在使用 UNIX 版本的 perl。谢谢你让我知道死是个例外。我没有意识到
    • @doon,相比之下,有exit。但通常你想要die,因为如果人们愿意,它允许人们处理错误。
    • @doon,另外请注意,SIGALRM 通常会终止您的进程,因此如果您不使用local $SIG{ALRM},您的脚本将被强制存在。不过,使用 local $SIG{ALRM} 可以让你的析构函数运行。
    【解决方案2】:

    来自报警器

       alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.
    

    sigalarm 交付之前,执行到达 else 块。在 sysread 之前插入 STDIN,以便 sigalarm 触发预期的结果。

    【讨论】:

      【解决方案3】:

      “而不是 sysread,我有一个 perl 脚本,它会休眠 30 秒。我的 超时设置为 10 秒。正如所料,#timed out 中的代码是 已执行,但脚本继续休眠。”

      真的吗?

      #!/usr/bin/perl
      use strict;
      use warnings FATAL => qw(all);
      
      eval {
          open my $fh, '<', $0 || die;
          local $SIG{ALRM} = sub {
              print STDERR "hello!\n";
              die "bye!";
          };
          alarm 3;
          while (<$fh>) {
              print $_;
              sleep 1;
          }
          close $fh;
      };
      
      if ($@) {
          print "HERE: $@\n";
      }
      

      输出:

      #!/usr/bin/perl
      use strict;
      use warnings FATAL => qw(all);
      hello!
      HERE: bye! at ./test.pl line 9, <$fh> line 3.
      

      在预期的 3 秒内结束;如果我只使用“sleep 100”而不是读取文件,情况仍然如此。请注意,如果您产生一个子进程, 警报不会杀死它,父进程必须等待。在这种情况下,“你好!”当警报触发时,信号处理程序中将出现,但捕获骰子的 eval 将在子进程完成之前完成。

      【讨论】:

      • 谢谢。这很有用。您在闹钟后编写的代码,即睡眠程序与我所做的相同。但它是在一个单独的脚本中。感谢您评论 eval 在命令完成之前不会完成。虽然这令人困惑。如果脚本中包含 eval 的父进程完成,则 eval 必须完成。我希望你能理解我的询问。评论框不允许我直观地解释。再次感谢
      • 如果您将open $0 替换为open my $fh, '-|', 'sleep 10' || die;,您就会明白我所说的必须等待子进程(shell 睡眠)的意思。但除此之外,不,正常的命令不应该阻止警报。
      【解决方案4】:

      我在将 Linux Perl 脚本移植到 Windows 时遇到了同样的问题。

      我通过...解决了它

      创建非阻塞套接字

      $recsock = IO::Socket::INET->new(
                                   LocalPort => 68,
                                   Proto => "udp",
                                   Broadcast => 1,
                                  Blocking => 0,
                                   ) or die "socket: $@";
      

      将 $continue 变量添加到超时句柄

      # Timeout handle
      $SIG{ALRM} = sub { 
          print "timeout\n";
          $continue = 1;
      };
      

      并在超时发生时检查 $continue 是否变为真:

          alarm($timeout);
           while(1){
               $recsock->recv($newmsg, 1024);
               eval {
                  $packet = Net::Package->new($newmsg); 
                   ...
              };
              sleep 0.1;
              last if ($continue);
          }
          alarm(0);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-01
        • 1970-01-01
        • 2018-03-19
        • 2014-07-04
        • 1970-01-01
        • 2019-02-26
        • 1970-01-01
        • 2012-06-02
        相关资源
        最近更新 更多