ОсобенностьПроцесс респаун и обработка сигнала в PHP
У меня есть проблема в PHP, когда respawned процессы не обработки сигналов, в то время, прежде чем паки, обработка работают правильно. Я сузил свой код на очень простой:
declare(ticks=1);
register_shutdown_function(function() {
if ($noRethrow = ob_get_contents()) {
ob_end_clean();
exit;
}
system('/usr/bin/nohup /usr/bin/php '.__FILE__. ' 1>/dev/null 2>/dev/null &');
});
function handler($signal)
{
switch ($signal) {
case SIGTERM:
file_put_contents(__FILE__.'.log', sprintf('Terminated [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
ob_start();
echo($signal);
exit;
case SIGCONT:
file_put_contents(__FILE__.'.log', sprintf('Restarted [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
exit;
}
}
pcntl_signal(SIGTERM, 'handler');
pcntl_signal(SIGCONT, 'handler');
while(1) {
if (time() % 5 == 0) {
file_put_contents(__FILE__.'.log', sprintf('Idle [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
}
sleep(1);
}
Как вы можете видеть, это следующее:
- Регистрация функцию выключения, в котором возрождаются процесс с
nohup
(так, чтобы игнорироватьSIGHUP
когда родительские процессы) - Регистрация обработчика через
pcntl_signal()
дляSIGTERM
иSIGCONT
. Сначала просто запишите сообщение о том, что процесс был прерван, а второй приведет к респаунту процесса. Это достигается с помощью функцийob_*
, поэтому, чтобы передать флаг, что должно быть сделано в функции выключения - либо выйти, либо возродиться. - Запись некоторой информации о том, что сценарий «жив» для файла журнала.
Что происходит
Итак, я начинаю сценарий с:
/usr/bin/nohup /usr/bin/php script.php 1>/dev/null 2>/dev/null &
Затем в файле журнала, есть записи, как:
Idle [ppid=7171] [pid=8849]
Idle [ppid=7171] [pid=8849]
Давайте скажем, затем я делаю kill 8849
:
Terminated [ppid=7171] [pid=8849]
Таким образом, это успешное обращение с SIGTERM
(и сценарий действительно выходит). Теперь, если я вместо того, чтобы делать kill -18 8849
, то я вижу (18 является числовым значением для SIGCONT
):
Idle [ppid=7171] [pid=8849]
Restarted [ppid=7171] [pid=8849]
Idle [ppid=1] [pid=8875]
Idle [ppid=1] [pid=8875]
И поэтому: первый, SIGCONT
также обработан правильно, и, судя по последующим «Idle» сообщения, вновь порожденный экземпляр скрипта работает хорошо.
Update # 1: Я думал о вещах с ppid=1
(таким образом, init
глобальный процесс) и бесхозных процессов обработки сигналов, но это не так. Вот log part, который показывает, что процесс сироты (ppid=1
) не является причиной: когда рабочий запускается с помощью управления приложением, он также вызывает его командой system()
- так же, как и сами респауны рабочих. Но после того, как приложение управляет приложением, оно имеет ppid=1
и правильно реагирует на сигналы, а если рабочий респавнирует себя, новая копия не отвечает на них, кроме SIGKILL
. Таким образом, проблема появляется только тогда, когда рабочий респавент сам.
Обновление # 2: Я попытался проанализировать, что происходит с strace
. Теперь, вот два блока.
- Когда работник еще не respawned - strace output. Посмотрите на строки
4
и5
, это когда я отправляюSIGCONT
, таким образомkill -18
в процесс. И затем он запускает всю цепочку: записывая файл,system()
вызывается и выходит из текущего процесса. Когда работник уже был респарен сам по себе - strace output. Вот, посмотрите на строки
8
и9
- они появились после полученияSIGCONT
. Первое: похоже, что процесс все равно каким-то образом получает сигнал, а во-вторых, он игнорирует сигнал. Никаких действий не было сделано, но система уведомляла о том, чтоSIGCONT
был отправлен. Почему тогда процесс игнорирует его - это вопрос (потому что, если установка обработчика пользователя дляSIGCONT
не удалась, то он должен завершить выполнение, а процесс не закончился). Что касаетсяSIGKILL
, то выход для уже respawned работника, как:nanosleep({1, 0}, <unfinished ...> +++ killed by SIGKILL +++
, который указывает, что сигнал был получен, и сделал то, что он должен делать.
Проблема
В качестве процесса респаун, он не реагирует ни SIGTERM
, ни к SIGCONT
. Тем не менее, по-прежнему можно завершить его SIGKILL
(так, kill -9 PID
действительно завершает процесс). Например, для процесса выше как kill 8875
, так и kill -18 8875
ничего не будет делать (процесс будет игнорировать сигналы и продолжать вести журнал сообщений).
Однако я бы не сказал, что регистрирующие сигналы терпят неудачу полностью, поскольку он переопределяет не менее SIGTERM
(что обычно приводит к завершению, в то время как в этом случае оно игнорируется). Также я подозреваю, что ppid = 1
указывает на неправильную вещь, но я не могу сказать точно.
Кроме того, я пытался любой другой вид сигналов (на самом деле, это не имеет значения, что это код сигнала, результат был всегда одинаков)
Вопрос
Что может быть причина такого поведения? Правильно ли это способ, который я подражаю процессу? Если нет, то каковы другие параметры, которые позволят новому порожденному процессу правильно использовать обработчики пользовательских сигналов?
Я думал об этом, но это не тот случай. Вот [log part] (http://pastebin.com/mSHL0VZu), который показывает, что процесс сироты ('ppid = 1') не является причиной: когда рабочий запускается при помощи приложения, он также вызывает его с помощью' system() '- так же, как и сам респавр рабочих. Но после того, как приложение управляет приложением, он имеет «ppid = 1» и правильно отвечает на сигналы, а если рабочий респавнирует себя, новая копия не отвечает на них, кроме 'SIGKILL'. Таким образом, проблема возникает только тогда, когда ** работник сам восстает **. –
Я провел несколько тестов с использованием промежуточного сценария оболочки, и это похоже на то, что вы говорите: 'ppid = 1' не проблема, так как работает первый икру, второй - нет.Пока у меня больше нет идей. – leafnode
В любом случае, я добавил некоторый анализ того, что делает процесс. –