Как прервать выполнение и убить дочерние процессы
Я пытаюсь вызвать длительную команду оболочки внутри скрипта PHP CLI с помощью exec()
. Но я ни за что на свете не могу понять, как прервать PHP-скрипт и убить порожденный дочерний процесс(ы). Похоже, как только я вызываю exec()
, мой обработчик сигналов игнорируется. Следующий код работает так, как я и ожидал; если я отправлю SIGTERM процессу, он повторит SIGTERM
и немедленно завершит работу.
<?php
declare(ticks = 1);
function sig_handler($signo) {
switch ($signo) {
case SIGTERM:
echo 'SIGTERM' . PHP_EOL;
flush();
break;
default:
}
}
pcntl_signal(SIGTERM, 'sig_handler', false);
sleep(60);
?>
Однако, если я заменю sleep(60);
на exec('sleep 60');
, я не достигну своего обработчика сигналов до тех пор, пока сон заканчивается. У меня есть два вопроса:
- Как я могу заставить сигналы работать с
exec
(илиshell_exec
илиproc_open
)? - После захвата сигнала, как я могу убить любые дочерние процессы, порожденные
exec
?
1 answers
В документации действительно говорится, что:
Если программа запускается с этой функцией, для того, чтобы она продолжала работать в фоновом режиме, вывод программы должен быть перенаправлен в файл или другой поток вывода. Невыполнение этого требования приведет к зависанию PHP до завершения выполнения программы.
(Курсив мой.) Я думаю, что повешение относится и к обработчикам сигналов.
Чтобы иметь возможность управлять дочерним процессом, он выглядит как будто вы должны выполнять их старомодным способом, с помощью fork+exec:
switch ($pid = pcntl_fork()) {
case -1: // failed to create process
die('Fork failed');
case 0: // child
pcntl_exec($path,$args);
die('Exec failed');
}
Как только родительский процесс получит идентификатор дочернего процесса в $pid
, он может отправить SIGINT дочернему процессу с posix_kill($pid, SIGINT)
.
Обновление: по-видимому, вы также могли бы использовать proc_open
и proc_terminate
.