Proc terminate: примеры (PHP)

Функция 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

Функция 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
JavaScript (Node.js)
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
Bash/Shell
#!/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);
?>
Результат: Неудача
Использование неподдерживаемого сигнала на Windows
<?
$process = proc_open('timeout 30', $descriptor, $pipes);

// SIGTERM (15) не поддерживается на Windows
// Работает только SIGKILL (9)
$result = proc_terminate($process, 15);
?>
Сигнал не отправлен (на Windows)

Изменения в версиях PHP

PHP 8.0.0

Параметр signal стал опциональным. Раньше требовалось всегда указывать значение, даже если использовался сигнал по умолчанию.

PHP 8.2.0

Улучшена обработка ошибок при передаче неверных аргументов. Более информативные сообщения об ошибках при попытке завершения несуществующих процессов.

Расширенные примеры

Управление группой процессов
Пример php
<?
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
)
Обработка таймаутов выполнения
Пример php
<?
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] => Таймаут выполнения
)
Гибридный подход с SIGTERM и SIGKILL
Пример php
<?
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 ? 'Успешно' : 'С ошибкой');
?>
Процесс завершен: Успешно

PHP proc_terminate function comments

En
Proc terminate Kills a process opened by proc_open