Как ждать завершения процесса (а также ждать соответствующих дочерних процессов) в PHP? - PullRequest
1 голос
/ 08 декабря 2011

У меня есть программа, которая обычно запускается до ее завершения, но в некоторых ситуациях она создает два дочерних процесса, и основной процесс затем завершается.Дочерние процессы будут по-прежнему выполняться после выхода из основного процесса.

Я хочу вызвать эту программу на PHP.

Первоначально я использовал следующую стратегию (не действительный код php),

$process=proc_open("python xxx.py");
while (1) {
   sleep(5);
   $status = proc_get_status($process);
   if (!$status['running']) {
       exit_loop;
   }
   if (timeout) {
      proc_terminate($process, 9);
      exit_loop;
   }
}

Но вскоре я обнаружил ошибку, заключающуюся в том, что процесс сразу же завершился, что $ status ['running'] равно false, но дочерний процесс все еще выполняется.Как на самом деле ждать всех дочерних процессов?

2-й вопрос: я не изучал proc_terminate, но я также заметил, что proc_terminate может работать не так, как я ожидал.Это потому, что $ process это bash, а не реальный процесс python?Это также может быть связано с тем, что то, что я завершил, является только родительским основным процессом, но не дочерним процессом.

Кто-нибудь посоветует мне, является ли приведенный выше код надежным кодом PHP или нет?Есть ли лучший способ справиться с этим?

Спасибо.

=====================

Подробнее,

  1. Я запускаю эту программу в оболочке (CLI) и не связана с функциями CGI.Поэтому мне не нужно беспокоиться о max_execution_time.

  2. Я хочу дождаться дочерних процессов, созданных основным процессом, открытым proc_open, я создам только один процесс в программе.

  3. Программа Python, которую я хочу вызвать, может зависать очень долго, поэтому я хочу проверить время ожидания и завершить его.

Ответы [ 3 ]

2 голосов
/ 08 декабря 2011

proc_get_status может получить pid дочернего процесса

$status = proc_get_status($process);
$pid = $status["pid"]

, затем вы можете использовать pcntl_waitpid, чтобы дождаться выхода дочернего процесса

pcntl_waitpid($pid, $child_status);

Но вам нужно скомпилировать phpс расширением pcntl.

См. Функции PCNTL

ОБНОВЛЕНИЕ: Если вы хотите уничтожить дочерний процесс по истечении времени ожидания

Во-первых: установите обработчик SIGALARM в вашем основном процессе

function term_proc() {
   // terminate your all child process
}

pcntl_signal(SIGALARM, term_proc);

Во-вторых: используйте pcntl_alarm, чтобы послать сигнал тревоги через несколько секунд

pcntl_alarm($seconds)

Для второго вопроса, proc_terminate будет работать независимо от того, какой процесс будет открыт.

2 голосов
/ 08 декабря 2011

Если вы не собираетесь каким-либо образом взаимодействовать с порожденным процессом (например, перенаправляя его ввод или вывод), тогда вы можете использовать system вместо proc_open.system будет ожидать завершения порожденного процесса, поэтому нет необходимости каждый раз проверять его.

Будьте осторожны: Ожидание другого процесса может привести к завершению вашего скриптаиз-за превышения max_execution_time.Это также может плохо работать с внутренним управлением исполнительных блоков вашего веб-сервера.

Обновление: Кажется, что вы хотите запустить процесс X, а затем дождаться всех процессов, которые обрабатывают сам Xзапускает для выхода перед продолжением.Мне жаль быть носителем плохих новостей, но PHP не предназначен для этого типа работы, и то, что вы ищете, невозможно достичь с помощью стандартного PHP.Конечно, было бы возможно, чтобы пользовательское расширение было написано на C, которое предоставляет эту функциональность, но, насколько я знаю, такого расширения нет в открытом доступе.

1 голос
/ 08 декабря 2011

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

  • cgroups обеспечиваетдостаточно механизма, чтобы systemd мог следить за всеми дочерними процессами, запущенными из одного начального execve(2) вызова.

    Хотя вы можете использовать cgroups самостоятельно, я мало что узнал опоследствия использования cgroups для управления процессами, в то время как systemd или libvirt или другие инструменты могут делать подобные вещи.(Это мое невежество больше, чем ограничение cgroups - возможно, оно прекрасно с этим справляется. Мне нравится мечтать.)

  • Откройте pipe(2) в своем процессе и прочитайтеконец pipe(7), созданного для дочернего процесса в файловом дескрипторе, достаточно высоком, чтобы он не использовался «случайно».(dup2(fd[0], 20), вероятно, хорошо.) Убедитесь, что другие ваши программы не закрывают неожиданные файловые дескрипторы. Установите флаг O_NONBLOCK в конце записи (fd[1]), используя fcntl(2) 's F_SETFL команда.

    Периодически пытайтесь записать байт в канал.В конечном итоге вы либо получите EPIPE сообщения об ошибках, указывающие, что конец чтения был закрыт всеми дочерними элементами, либо вы получите сообщение об ошибке с errno, установленным в EAGAIN, что означает, что канал равен full и все еще бежит ребенок.

...