PHP - Убить дочерний процесс, запущенный pcntl_fork

Я использую pcntl_fork для запуска дочернего процесса для отправки электронной почты через SMTP.

Дочерний процесс использует пакет PEAR Mail для отправки электронной почты, но проблема в том, что если удаленный сервер не отвечает, процесс просто работает вечно, ожидая ответа, независимо от любого ограничения по времени, установленного в php.ini.

Чтобы обойти это, я использую функцию pcntl_alarm для запуска функции через 30 секунд, которая убивает дочерний процесс, если он все еще работает.

function handlesig($sig) {
    global $pid,$node,$resend;
    posix_kill($pid,SIGKILL);
    mysql_query("insert into log (event) values ('Timed out!')");
}

Когда я убиваю дочерний процесс, в системе остается несуществующий процесс.

Есть ли другой сигнал, который я должен использовать, который все равно заставит дочерний процесс умереть, не дожидаясь соединения (потому что соединение никогда не завершится) и избежать создания несуществующих процессов?


person Tim    schedule 12.11.2009    source источник


Ответы (5)


Вам нужно либо дождаться его, как описано выше, либо заставить дочерний процесс отсоединиться от родителя, используя posix_setsid

person StasM    schedule 21.12.2009

Если у вас есть дети-зомби, это означает, что вы не wait работаете для них. Не эксперт по PHP, но системный вызов waitpid(2). например вызов

waitpid(-1, NULL, WNOHANG);

время от времени или всякий раз, когда вы получаете SIGCHILD. Каждый звонок будет пожинать до одного зомби. WNOHANG заставляет его возвращаться сразу, а не блокировать, если нет завершенных дочерних элементов.

На справочной странице wait есть хороший раздел заметок о том, как все это работает...

person Peter Cordes    schedule 11.12.2009

Чтобы завершить ответ Питера, взгляните на pcntl_waitpid.

Просто нужно сделать это после убийства: pcntl_waitpid($pid)

person goneri    schedule 18.12.2009

Обычное решение этой проблемы — использование хорошо сконфигурированного локального ретранслятора. Что я обычно делаю, так это настраиваю ретранслятор локального хоста с помощью постфикса.

Затем я отправлю электронное письмо на локальный ретранслятор, который поставит его в очередь и отправит получателю как можно скорее.

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

Что касается вашего вопроса о несуществующем, насколько я знаю, SIGKILL - очень сложный метод для завершения процесса, вы пробовали (SIGSTOP или SIGTSTP)?

person user207675    schedule 12.11.2009
comment
Причина, по которой я отправляю напрямую через PHP, заключается в том, что я могу получить ответ и обработать его соответствующим образом для успеха/мягкого отскока/жесткого отскока. SIGTERM и SIGKILL — единственные известные мне сигналы, а SIGTERM не работал, поэтому я использовал SIGKILL. Я попробую SIGSTOP/SIGTSTP. - person Tim; 12.11.2009
comment
Хм, лично я бы сделал это в двух отдельных сценариях. Один для отправки через локальный smtp-сервис, второй для обработки отказов, своего рода автоматический скрипт pop/get, который обрабатывает отказы и успех. Одна из причин этого заключается в том, что php-скрипт, который доставляет почту на smtp, не блокирует дочерние элементы веб-сервера. Например, если у вас есть максимум 20 дочерних элементов, и каждый дочерний элемент начинает подключение к внешнему источнику, никакая другая служба не будет доступна, пока дочерний элемент не будет снова освобожден. - person user207675; 13.11.2009

exec(PATH_TO_PHP . " email_script.php $params > /dev/null 2>&1 &");

Таким образом, родитель не ждет ответа от ребенка, и у вас нет зомби. В email_script.php обрабатывайте отправку электронной почты и регистрируйте ответ в файле, который вы можете анализировать из своего родительского скрипта или чего-то еще.

person agentile    schedule 15.11.2009