Как правильно выполнить форк с mod_perl2?

У меня проблемы с разветвлением длительного процесса из некоторого кода, работающего под mod_perl2.

По большей части все работает, но кажется, что разветвленный процесс держит открытые дескрипторы файлов журнала Apache - это означает, что Apache не перезапускается, пока процесс запущен (я получаю сообщение «Не удалось открыть файлы журнала»).

Вот код, который я использую:

use POSIX; # required for setsid

# Do not wait for child processes to complete
$SIG{CHLD} = 'IGNORE';

# fork (and make sure we did!)
defined (my $kid = fork) or die "Cannot fork: $!\n";

if ($kid) {
    return (1, $kid);
}else {
    # chdir to /, stops the process from preventing an unmount
    chdir '/' or die "Can't chdir to /: $!";

    # dump our STDIN and STDOUT handles
    open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
    open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";

    # redirect for logging
    open STDERR, '>', $log_filename or die "Can't write to log: $!";

    # Prevent locking to apache process
    setsid or die "Can't start a new session: $!";

    # execute the command
    exec( $cmd, @args );

    die "Failed to exec";
}

Еще во времена mod_perl1 я помню, как использовал $r->cleanup_for_exec для решения этой проблемы, но, похоже, он не поддерживается в mod_perl2. (Изменить: Очевидно, что это больше не требуется .. )

Мы будем очень благодарны за любые советы о том, как правильно запустить длительный процесс из mod_perl2 без этих проблем!


person Dan    schedule 23.01.2009    source источник
comment
Привет, Дэн, как ты решил эту проблему? У меня та же проблема. Раньше я использовал mod_perl1, и $r->cleanup_for_exec работал нормально, но в mod_perl2 это больше не требуется, не могли бы вы помочь мне реализовать это в mod_perl2? Заранее спасибо.   -  person Nikhil Jain    schedule 14.06.2013


Ответы (3)


Вероятно, вы захотите прочитать это обсуждение. Кажется, вам не стоит форкнуть mod_perl, если вы не знаете, как что-то подготовить. Вы должны использовать такой модуль, как Apache2 :: SubProcess

person Leon Timmermans    schedule 23.01.2009

Попробуйте закрыть дескрипторы STDIN / STDOUT перед вилкой.

person nezroy    schedule 23.01.2009
comment
Это полностью сломает Apache .. Родительский процесс по-прежнему должен выдать ответ (и отправить его через STDOUT) для клиента .. - person Dan; 23.01.2009
comment
Родительский процесс может отправить свой ответ перед разветвлением; если вы на самом деле не выполняете больше работы в родительском элементе после вилки. Если файлы журнала не находятся в дескрипторах STDIN / OUT / ERR, вы можете просто начать закрывать любые открытые дескрипторы ›2, обнаруженные в дочернем процессе. - person nezroy; 23.01.2009
comment
Да, проблема в том, что процесс apache продолжает обслуживать другие запросы, когда он завершает этот, убивая его, STDERR нарушает его ведение журнала, и я подозреваю, что убийство его STDIN останавливает его от связи с родительским процессом. В любом случае - попробовал, и это не решает проблему;) - person Dan; 23.01.2009
comment
Попробуйте просто выполнить цикл от 3 до 1024 в дочернем элементе, выполнив 'open ($ fh, ‹& = #); close ($ fh); ', где # - это idx цикла. Выявление и игнорирование ошибок. В идеале это должно закрыть все оставшиеся открытые дескрипторы. Возможно, это не постоянное исправление, но потенциально может подтвердить, что проблема заключается в открытых дескрипторах. - person nezroy; 23.01.2009
comment
Кроме того, еще во времена демонизации Perl было разумным выполнить форк дважды. Не помню почему, просто это помогло предотвратить бесхозные процессы. Стоит попробовать :) - person nezroy; 23.01.2009
comment
Мм ... дважды вилка может быть не так уж и ясна. Это будет одна вилка, а затем дочерняя вилка снова, а потомок второй вилки - процесс, который нужно сохранить. Затем выйдет дочерний элемент первой вилки. - person nezroy; 23.01.2009
comment
Да, вам нужно выполнить форк дважды: один раз перед setsid (для создания нового процесса) и еще раз после setsid (чтобы убедиться, что ваш сеанс и группа процессов не имеют ведущего процесса). - person Leon Timmermans; 23.01.2009

В моем коде (ранее mod_perl, теперь FCGI) в предложении else в if ($ kpid) указано,

    close STDIN;
    close STDOUT;
    close STDERR;
    setsid();

Кроме того, по причинам, о которых я забыл, я немедленно снова выполняю форк, а затем в этом потомке повторно открываю STDIN, STDOUT и STDERR.

Так это выглядит:

$SIG{CHLD} = 'IGNORE';

# This should flush stdout.
my $ofh = select(STDOUT);$| = 1;select $ofh;

my $kpid = fork;
if ($kpid)
{
    # Parent process
    waitpid($kpid, 0);
}
else
{
    close STDIN;
    close STDOUT;
    close STDERR;
    setsid();
    my $gpid = fork;
    if (!$gpid)
    {
        open(STDIN, "</dev/null") ;#or print DEBUG2 "can't redirect stdin\n";
        open(STDOUT, ">/dev/null") ;#or print DEBUG2 "can't redirect stdout\n";
        open(STDERR, ">/dev/null") ;#or print DEBUG2 "can't redirect stderr\n";
        # Child process
        exec($pgm, @execargs) ;# or print DEBUG2 "exec failed\n";
    }
    exit 0;
}
person Paul Tomblin    schedule 23.01.2009