Как ждать процесса внука (в Perl `bash` становится равным -1 из-за SIG CHLD) - PullRequest
2 голосов
/ 05 июня 2019

У меня есть Perl-скрипт (фрагмент ниже), который запускается в cron для выполнения системных проверок.Я разрываю ребенка как тайм-аут и пожинаю его с помощью SIG {CHLD}.Perl выполняет несколько системных вызовов скриптов Bash и проверяет их состояние выхода.Один сценарий bash дает сбой около 5% времени без ошибок.Сценарии Bash существуют с 0, а Perl видит $?как -1 и $!как "Нет дочерних процессов".

Этот сценарий bash проверяет лицензии компилятора, а Intel icc остается после завершения сценария Bash (вывод ps ниже).Я думаю, что icc zombie завершает работу, заставляя Perl использовать обработчик SIG {CHLD}, который уничтожает $?статус, прежде чем я смогу его прочитать.

Compile status -1; No child processes

#!/usr/bin/perl
use strict;
use POSIX ':sys_wait_h';

my $GLOBAL_TIMEOUT = 1200;

### Timer to notify if this program hangs
my $timer_pid;
$SIG{CHLD} = sub {
    local ($!, $?);
    while((my $pid = waitpid(-1, WNOHANG)) > 0)
    {
        if($pid == $timer_pid)
        {
            die "Timeout\n";
        }
    }
};

die "Unable to fork\n" unless(defined($timer_pid = fork));
if($timer_pid == 0)  # child
{
    sleep($GLOBAL_TIMEOUT);
    exit;
}
### End Timer

### Compile test
my @compile = `./compile_test.sh 2>&1`;
my $status = $?;
print "Compile status $status; $!\n";
if($status != 0)
{
    print "@compile\n";
}

END  # Timer cleanup
{
    if($timer_pid != 0)
    {
        $SIG{CHLD} = 'IGNORE';
        kill(15, $timer_pid);
    }
}

exit(0);
#!/bin/sh

cc compile_test.c
if [ $? -ne 0 ]; then
    echo "Cray compiler failure"
    exit 1
fi

module swap PrgEnv-cray PrgEnv-intel
cc compile_test.c
if [ $? -ne 0 ]; then
    echo "Intel compiler failure"
    exit 1
fi

wait
ps
exit 0

Ожидание на самом деле не ждет, потому что cc вызывает icc, который создает процесс внука зомби, который ждет (или ждать PID) нене блокировать для.(подождите `pidof icc`, 31589 в данном случае, выдает" не дочерний элемент этой оболочки ")

user 31589     1  0 12:47 pts/15   00:00:00 icc

Я просто не знаю, как это исправить в Bash или Perl.

Спасибо, Крис

Ответы [ 2 ]

1 голос
/ 07 июня 2019

Я подумал, что самым быстрым решением было бы добавить сон на секунду или две внизу скрипта bash, чтобы дождаться завершения зомби icc.Но это не сработало.

Если у меня еще не было SIG ALRM (в настоящей программе), я согласен, что лучшим выбором будет завернуть все это в eval.Даже подумал, что это будет довольно уродливо для программы из 500 строк.

Без локального ($?), каждый `системный вызов получает $?= -1$?Мне нужно в этом случае после waitpid, а затем, к сожалению, установить -1 после выхода из обработчика sig.Так что я считаю, что это работает. Новые строки отображаются с ###

my $timer_pid;
my $chld_status;    ###
$SIG{CHLD} = sub {
    local($!, $?);
    while((my $pid = waitpid(-1, WNOHANG)) > 0)
    {
        $chld_status = $?;    ###
        if($pid == $timer_pid)
        {
            die "Timeout\n";
        }
    }
};

...
my @compile = `./compile_test.sh 2>&1`;
my $status = ($? == -1) ? $chld_status : $?;    ###
...
1 голос
/ 05 июня 2019

Разве это не вариант использования для alarm? Бросьте ваш обработчик SIGCHLD и скажите

local $? = -1;
eval {
    local $SIG{ALRM} = sub { die "Timeout\n" };
    alarm($GLOBAL_TIMEOUT);
    @compile = `./compile_test.sh 2>&1`;
    alarm(0);
};

my $status = $?;

вместо.

...