Компонент Symfony2 Process - невозможно создать канал и запустить новый процесс

Я использую компонент Symfony2 Process для ручного управления пулом процессов.

В приведенном ниже примере я перезапускаю 2 простых процесса каждые 2 секунды и отслеживаю, что происходит. Приложение ломается после перезапуска этих процессов несколько сотен раз.

Выполнение остановлено, и я получаю следующее предупреждение PHP:

proc_open(): unable to create pipe Too many open files

и затем компонент Symfony Process создает следующее исключение:

[Symfony\Component\Process\Exception\RuntimeException]  
Unable to launch a new process.   

Я вручную отслеживал общее количество открытых процессов, и он никогда не превышал ожидаемый предел.

Приведенный ниже упрощенный фрагмент является частью команды Symfony2 и запускается из CLI (например, app/console hamster:run):

    $processes[] = new Process("ls > /dev/null", null, null, null, 2);
    $processes[] = new Process("date > /dev/null", null, null, null, 2);

    while (count($processes) > 0) {
        foreach ($processes as $i => $process) {
            if (!$process->isStarted()) {
                $process->start();

                continue;
            }

            try {
                $process->checkTimeout();
            } catch (\Exception $e) {
                // Don't stop main thread execution
            }

            if (!$process->isRunning()) {
                // All processes are timed out after 2 seconds and restarted afterwards
                $process->restart();
            }
        }

        usleep($sleep * 1000000);
    }

Это приложение работает на сервере MAC под управлением OS X 10.8.4.

Буду признателен за любые советы о том, как продолжить корень этой проблемы.

Обновление № 1: я упростил свою функцию для работы с основными командами, такими как ls а также date для более быстрого тестирования. Выглядит так, будто команда Process завершается неудачно после запуска и остановки около 1000-1500 процессов.

Я подозревал что proc_close() неправильно вызывался для каждого процесса, но дальнейшее расследование показало, что это не так.

1 ответ

Решение

Дескрипторы файла не собираются мусором, поэтому они в конечном итоге заполняются (os os или php limit, не уверены), но вы можете это исправить, добавив явный вызов сборки мусора:

gc_collect_cycles();
usleep($sleep * 1000000);

Кроме того, имейте в виду, что сборка мусора не очень хорошо работает внутри foreach цикл из-за способа, которым php отображает временный $foo as $bar => $var массив переменных в память. Если вам нужно это в той части кода, вы могли бы вместо этого переключиться на что-то вроде этого, что, я думаю, должно позволить сборку мусора внутри for цикл:

$processes[] = new Process("ls > /dev/null", null, null, null, 2);
$processes[] = new Process("date > /dev/null", null, null, null, 2);

$sleep = 0;

do {
    $count = count($processes);
    for($i = 0; $i < $count; $i++) {
        if (!$processes[$i]->isStarted()) {
            $processes[$i]->start();

            continue;
        }

        try {
            $processes[$i]->checkTimeout();
        } catch (\Exception $e) {
            // Don't stop main thread execution
        }

        if (!$processes[$i]->isRunning()) {
            // All processes are timed out after 2 seconds and restarted afterwards
            $processes[$i]->restart();
        }

        gc_collect_cycles();
    }

    usleep($sleep * 1000000);
} while ($count > 0);
Другие вопросы по тегам