Почему не умирает обработчик сигнала тревоги, убивающий процесс?

Из Как я могу указать лимит времени ожидания для системного вызова 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() используется для выхода из вашего скрипта и отображения сообщения для чтения пользователем». Однако в случае с приведенным выше сценарием сценарий будет обрабатывать код с #тайм-аутом. Также sysread продолжает работать. Вместо sysread у меня был perl-скрипт, который спал 30 секунд. Мой тайм-аут был установлен на 10 секунд. Как и ожидалось, код в #timed out выполняется, но скрипт продолжает спать. Любые входные данные приветствуются.


person doon    schedule 18.04.2012    source источник


Ответы (4)


die не приводит к завершению процесса, а генерирует исключение.

Теперь, если ничто не перехватывает исключение, это завершает процесс, но у вас есть код для перехвата этого самого исключения. .

Процесс не заканчивается, потому что вы явно предотвращаете его завершение.


Поскольку вы не совсем понимаете, какое поведение вы получаете, может быть еще одна возможность: вы используете сборку Perl для Windows.

alarm — это системный вызов Unix. Сама цель (отправка сигнала по прошествии определенного времени) не имеет смысла в Windows, поскольку в Windows нет сигналов.

Perl до некоторой степени эмулирует alarm, но только в очень ограниченной степени. sleep вполне может быть единственной операцией, которую можно прервать с помощью alarm. В противном случае тайм-аут проверяется только между операторами.

Таким образом, он не будет прерывать sysread, но как только sysread вернется, Perl заметит, что время ожидания истекло, и затем эмулирует сигнал.

person ikegami    schedule 18.04.2012
comment
@doon, добавлен альтернативный ответ. - person ikegami; 18.04.2012
comment
Спасибо. Я использую версию Perl для UNIX. Спасибо, что сообщили мне, что умереть — это исключение. я этого не понимал - person doon; 18.04.2012
comment
@doon, напротив, есть exit. Но обычно вам нужен die, потому что он позволяет людям обрабатывать ошибки, если они того пожелают. - person ikegami; 18.04.2012
comment
@doon, также обратите внимание, что SIGALRM обычно убивает ваш процесс, поэтому ваш скрипт будет принудительно существовать, если вы не использовали local $SIG{ALRM}. Однако использование local $SIG{ALRM} позволяет запускать ваши деструкторы. - person ikegami; 18.04.2012

От мужской тревоги

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

Перед доставкой sigalarm выполнение достигает блока else. Вставьте STDIN перед sysread, чтобы sigalarm вызывал ожидаемые результаты.

person tuxuday    schedule 18.04.2012

«Вместо 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 секунды; это все еще так, если я просто использую «сон 100» вместо чтения файла. Обратите внимание, что если вы порождаете подпроцесс, сигнал тревоги не уничтожит его, и родительский процесс должен ждать. В этом случае "привет!" в обработчике сигнала появится, когда сработает сигнал тревоги, но eval, который ловит кубик, не завершится, пока не завершится подпроцесс.

person CodeClown42    schedule 18.04.2012
comment
Спасибо. Это полезно. Код, который вы написали после будильника, то есть процедуры сна, такой же, как и я. Но это было в отдельном сценарии. Спасибо за комментарий, что eval не завершится, пока команда не завершится. Хотя это сбивает с толку. Если родительский процесс, в скрипте которого есть eval, завершается, то и eval должен завершиться. Надеюсь, вы поняли мой запрос. Поле для комментариев не позволяет мне объяснить визуально. Спасибо еще раз - person doon; 18.04.2012
comment
Если вы замените open $0 на open my $fh, '-|', 'sleep 10' || die;, вы увидите, что я имею в виду под необходимостью ожидания подпроцесса (оболочка спит). Но в остальном нет, нормальная команда не должна блокировать тревогу. - person CodeClown42; 18.04.2012

У меня была такая же проблема при портировании скрипта 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);
person Stef    schedule 24.06.2013