Proc terminate: примеры (PHP)
proc_terminate(resource $process, int $signal = 15): boolОсновы функции proc_terminate
Функция proc_terminate отправляет сигнал завершения процессу, открытому с помощью proc_open(). Она позволяет программно остановить выполнение внешней команды, не дожидаясь ее естественного завершения.
Функция применяется для принудительного завершения долго выполняющихся процессов, обработки тайм-аутов, управления ресурсами или реализации механизмов отмены операций. Это полезно в сценариях, где внешняя программа может зависнуть или превысить выделенное время выполнения.
- process (resource) - ресурс процесса, возвращаемый функцией proc_open().
- signal (int, необязательный) - номер сигнала для отправки процессу. По умолчанию используется SIGTERM (15). В Windows поддерживается только сигнал SIGKILL (9).
Возвращает true при успешной отправке сигнала или false при возникновении ошибки.
Простые примеры использования
<?
$descriptor = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"] // stdout
];
$process = proc_open('sleep 30', $descriptor, $pipes);
if (is_resource($process)) {
echo "Процесс запущен\n";
sleep(2);
// Завершаем процесс
$result = proc_terminate($process);
echo "Сигнал отправлен: " . ($result ? 'Да' : 'Нет') . "\n";
proc_close($process);
}
?>Процесс запущен Сигнал отправлен: Да
<?
$process = proc_open('tail -f /var/log/syslog', $descriptor, $pipes);
if (is_resource($process)) {
// Отправляем SIGINT (2) - аналог Ctrl+C
proc_terminate($process, 2);
// На Windows можно использовать только SIGKILL
// proc_terminate($process, 9);
}
?>Процесс завершен сигналом SIGINT
Альтернативные функции в PHP
proc_close() ожидает завершения процесса и закрывает все связанные с ним ресурсы. В отличие от proc_terminate, эта функция блокирует выполнение до завершения процесса.
Функция posix_kill() отправляет сигнал процессу по его PID. Требует расширения POSIX и работает только в Unix-подобных системах. Более гибкая, чем proc_terminate, так как может работать с любыми процессами системы.
proc_terminate предпочтительнее для завершения процессов, открытых через proc_open. posix_kill используется для работы с любыми системными процессами. proc_close применяется для корректного завершения с ожиданием.
Аналоги в других языках программирования
Proc terminate в Python
import subprocess
import signal
import time
# Запуск процесса
proc = subprocess.Popen(['sleep', '30'])
time.sleep(2)
# Завершение процесса
proc.terminate() # SIGTERM
# proc.kill() # SIGKILL
# Ожидание завершения
proc.wait()
print(f"Процесс завершен с кодом: {proc.returncode}")Процесс завершен с кодом: -15
const { spawn } = require('child_process');
const child = spawn('sleep', ['30']);
setTimeout(() => {
// Отправка SIGTERM
child.kill('SIGTERM');
// Или SIGKILL
// child.kill('SIGKILL');
}, 2000);
child.on('exit', (code, signal) => {
console.log(`Процесс завершен. Код: ${code}, Сигнал: ${signal}`);
});Процесс завершен. Код: null, Сигнал: SIGTERM
#!/bin/bash
# Запуск процесса в фоне
sleep 30 &
PID=$!
sleep 2
# Завершение процесса
kill $PID # SIGTERM
# kill -9 $PID # SIGKILL
# Ожидание завершения
wait $PID
echo "Процесс завершен с кодом: $?"Процесс завершен с кодом: 143
Типичные ошибки
<?
// Неправильно: передача не ресурса
$result = proc_terminate("not_a_resource");
var_dump($result);
?>Warning: proc_terminate() expects parameter 1 to be resource, string given bool(false)
<?
$process = proc_open('sleep 1', $descriptor, $pipes);
sleep(2); // Ждем завершения процесса
// Пытаемся завершить уже завершенный процесс
$result = proc_terminate($process);
echo "Результат: " . ($result ? 'Успех' : 'Неудача');
proc_close($process);
?>Результат: Неудача
<?
$process = proc_open('timeout 30', $descriptor, $pipes);
// SIGTERM (15) не поддерживается на Windows
// Работает только SIGKILL (9)
$result = proc_terminate($process, 15);
?>Сигнал не отправлен (на Windows)
Изменения в версиях PHP
Параметр signal стал опциональным. Раньше требовалось всегда указывать значение, даже если использовался сигнал по умолчанию.
Улучшена обработка ошибок при передаче неверных аргументов. Более информативные сообщения об ошибках при попытке завершения несуществующих процессов.
Расширенные примеры
<?
class ProcessManager {
private $processes = [];
public function startProcess($cmd) {
$descriptor = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open($cmd, $descriptor, $pipes);
if (is_resource($process)) {
$this->processes[] = [
'resource' => $process,
'pipes' => $pipes,
'started' => time()
];
return count($this->processes) - 1;
}
return false;
}
public function terminateProcess($id, $signal = 15) {
if (isset($this->processes[$id])) {
return proc_terminate(
$this->processes[$id]['resource'],
$signal
);
}
return false;
}
public function terminateAll($signal = 15) {
$results = [];
foreach ($this->processes as $id => $data) {
$results[$id] = proc_terminate($data['resource'], $signal);
}
return $results;
}
}
$manager = new ProcessManager();
$pid1 = $manager->startProcess('sleep 60');
$pid2 = $manager->startProcess('tail -f /dev/null');
sleep(3);
// Завершаем все процессы
$results = $manager->terminateAll();
print_r($results);
?>Array
(
[0] => 1
[1] => 1
)<?
function executeWithTimeout($cmd, $timeout) {
$descriptor = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open($cmd, $descriptor, $pipes);
if (!is_resource($process)) {
return ['error' => 'Не удалось запустить процесс'];
}
$startTime = time();
$status = proc_get_status($process);
while ($status['running']) {
if ((time() - $startTime) > $timeout) {
// Превышен таймаут - завершаем процесс
proc_terminate($process, 9); // SIGKILL
// Закрываем все каналы
foreach ($pipes as $pipe) {
fclose($pipe);
}
proc_close($process);
return ['error' => 'Таймаут выполнения'];
}
usleep(100000); // 100ms
$status = proc_get_status($process);
}
// Чтение вывода
$output = stream_get_contents($pipes[1]);
$error = stream_get_contents($pipes[2]);
foreach ($pipes as $pipe) {
fclose($pipe);
}
$exitCode = proc_close($process);
return [
'exit_code' => $exitCode,
'output' => $output,
'error' => $error
];
}
// Пример использования
$result = executeWithTimeout('sleep 10 && echo "Готово"', 3);
print_r($result);
?>Array
(
[error] => Таймаут выполнения
)<?
function gracefulTerminate($process, $gracefulTimeout = 5) {
// Сначала отправляем SIGTERM для graceful shutdown
if (!proc_terminate($process, 15)) {
return false;
}
$start = time();
$status = proc_get_status($process);
// Ждем graceful завершения
while ($status['running'] && (time() - $start) < $gracefulTimeout) {
usleep(100000);
$status = proc_get_status($process);
}
// Если процесс еще работает - отправляем SIGKILL
if ($status['running']) {
return proc_terminate($process, 9);
}
return true;
}
// Пример использования
$process = proc_open('some_long_running_script.php', $descriptor, $pipes);
$result = gracefulTerminate($process, 10);
echo "Процесс завершен: " . ($result ? 'Успешно' : 'С ошибкой');
?>Процесс завершен: Успешно