2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 Как ограничить время выполнения внешней программы?
Сообщение06.04.2015, 16:48 


25/11/08
449
В PHP скрипте запускаю внешнюю программу через shell_exec(). Но иногда она зависает. Как из скрипта запускать с возможностью прервать выполнение по истечении определенного времени?

Есть ли какие-то команды в shell, ограничивающие время работы процесса?

Если нет, то как можно организовать убийство процесса в PHP? Как отследить время? Sleep() с последующим убийством не подходит, т.к. при успешном завершении, необходимо тут же продолжить, а не спать.

Пока придумал только так
код: [ скачать ] [ спрятать ]
Используется синтаксис PHP
<?php
        $pipes = [];
        $options = [
            0 => ['pipe', 'r'],
            1 => ['pipe', 'w'],
            2 => ['pipe', 'w']
        ];
       
        $pid = proc_open('filename', $options, $pipes );
       
        $breaktime = microtime() + TIMELIMIT;
     
        while( microtime() < $breaktime )
        {
           $status = proc_get_status($pid);
           if( $status['running'] == false ) break;
           usleep(10); // ждем 10 микросекунд
        }
       
        proc_close($pid);
    ?>

Так будет нормально или такой частный цикл будет слишком нагружать? Как вообще положено убивать дочерние процессы по таймауту?

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение06.04.2015, 18:01 


11/12/14
893
Да собственно крутится в цикле ожидания по таймауту, как вы и написали, может только ждать не 10 а подольше, если нагрузка на проц упадёт.
Но тут ньюансы, смотрите описания к функциям внимательно и желательно читайте комментарии на php.net к функциям.
Вы например не делаете обязательного закрытия пайпов, без чего дочерний процесс может как раз зависнуть - это прямо написано в описании php_close.
Далее, если дело доходит всё таки до принудительной терминации, то обратите пристальное внимание на первый же пользовательский комментарий в http://php.net/manual/en/function.proc-terminate.php

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение06.04.2015, 22:01 


25/11/08
449
Цитата:
Вы например не делаете обязательного закрытия пайпов, без чего дочерний процесс может как раз зависнуть - это прямо написано в описании php_close.
Спасибо за совет. Я не обратил внимание на это.

Еще проблема возникла. Программа dvipng самоуничтожается, устанавливая в статус [running] => 0, а программа latex, видимо, нет. У него в статусе [running] => 1 установлен до конца, до убийства по таймауту. Может он не убивается и живет сам по себе как демон, принимая запросы? Как же в shell_exec() определяется, что пора выходить? Видимо, в shell_exec() ожидается вывод внешней программы.

Может как-то использовать stream_select. Так будет корректно работать? Не произойдет ли убийство раньше времени? :roll:

Код:
$pipes = array();
  $options = array(
     0 => array('pipe', 'r'),
     1 => array('pipe', 'w'),
     2 => array('pipe', 'w')
  );
 
  $pid = proc_open( $cmd, $options, $pipes, $cwd );

  $r_stms = array( $pipes[1], $pipes[2] );
  $w_stms = NULL;
  $e_stms = NULL;
 
  stream_select( $r_stms, $w_stms, $e_stms, $timelimit );

  fclose($pipes[0]);
  fclose($pipes[1]);
  fclose($pipes[2]);
  proc_close($pid);

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение06.04.2015, 23:47 


25/11/08
449
Похоже, stream_select вообще не ждет вывода, а почему-то сразу выходит.

код: [ скачать ] [ спрятать ]
Используется синтаксис PHP
$start = microtime(1);

echo '$start = '.$start.'<br>';

$pid = proc_open( 'sleep 30', $options, $pipes, $cwd )

$proc_status = proc_get_status($pid);

print_r($proc_status);

$r_stms = array($pipes[1],$pipes[2]);
$w_stms = NULL;
$e_stms = NULL;
$ch_strm = stream_select( $r_stms, $w_stms, $e_stms, 30 );

echo '$ch_strm = '.$ch_strm.'<br>';

fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($pid);

$end = microtime(1);
echo '$end = '.$end.'<br>';
echo '$d = '.($end-$start).'<br>';


Выводится следующее:
Код:
$start = 1428351165.01
Array ( [command] => sleep 30 [pid] => 6832 [running] => 1 [signaled] => [stopped] => [exitcode] => -1 [termsig] => 0 [stopsig] => 0 )
$ch_strm = 2
$end = 1428351165.06
$d = 0.0482149124146


-- Вт апр 07, 2015 00:49:14 --

Вопрос в том, как вызываемая программа сообщает shell_exec, что пора выходить. Вернее, как shell_exec узнает, что пора завершаться.

В мануале написано:
Цитата:
shell_exec — Выполняет команду через шелл и возвращает полный вывод в виде строки.

Возвращаемые значения
Вывод исполняемой команды или NULL, если произошла ошибка или команда ничего не вывела.

Замечание:
Эта функция может вернуть NULL и в случае ошибки и в случае, если программа ничего не вывела. Нельзя определить неудачный запуск с помощью этой функции. Если требуется получить код завершения программы используйте exec().

Как правильно это интерпретировать.. Можно ли утверждать, что shell_exec просто ждет вывода. Может программа как-то сообщает, что вывод закончен?

Или же shell_exec выходит, отдает выходной поток вызывающему скрипту, но при этом не убивает программу, давая возможность ей продолжать вывод?

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение07.04.2015, 02:07 
Заслуженный участник


02/08/11
7013
ellipse в сообщении #1001013 писал(а):
Как правильно это интерпретировать.. Можно ли утверждать, что shell_exec просто ждет вывода. Может программа как-то сообщает, что вывод закончен?
Нет, shell_exec просто ждёт завершения работы программы. И имено так программа и сообщает, что вывод закончен: просто заканчивает работу, завершает свой процесс.

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение07.04.2015, 02:24 


25/11/08
449
warlock66613 в сообщении #1001042 писал(а):
Нет, shell_exec просто ждёт завершения работы программы. И имено так программа и сообщает, что вывод закончен: просто заканчивает работу, завершает свой процесс.
Как она сообщает о завершении?

Вот такой отладочный код:
код: [ скачать ] [ спрятать ]
Используется синтаксис PHP
  $timelimit = 10; // 10 сек.
  $cmd = '/Server/texlive/2014/bin/win32/latex myfile.tex';
  $cwd = '/Server/htdocs/tex/tmp/';

  $pipes = array();
  $options = array(
     0 => array('pipe', 'r'),
     1 => array('pipe', 'w'),
     2 => array('pipe', 'w')
  );

  $start = microtime(1);
  echo '$start = '.$start.'<br>';
 
  $breaktime = microtime(1) + $timelimit;

  if( $pid = proc_open( $cmd, $options, $pipes, $cwd ) )
  {
    while( microtime(1) < $breaktime )
    {
      $proc_status = proc_get_status($pid);

      echo '<br>$proc_status = '; print_r($proc_status);

      if( $proc_status['running'] == false )
      {
        echo 'break!<br>';
        break;
      }
      usleep(200000);
    }
   
    $proc_status = proc_get_status($pid);
    echo '<br>$end_proc_status = ';  print_r($proc_status);
    echo '<br><br>';
   
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($pid);
  }
 
  $end = microtime(1);
  echo '$end = '.$end.'<br>';  
  echo '$d = '.($end-$start).'<br><br>';


выводит:

Код:
$start = 1428361887.33

$proc_status = Array ( [command] => /Server/texlive/2014/bin/win32/latex myfile.tex [pid] => 6420 [running] => 1 [signaled] => [stopped] => [exitcode] => -1 [termsig] => 0 [stopsig] => 0 )
$proc_status = Array ( [command] => /Server/texlive/2014/bin/win32/latex myfile.tex [pid] => 6420 [running] => 1 [signaled] => [stopped] => [exitcode] => -1 [termsig] => 0 [stopsig] => 0 )

и так еще 10050 раз. и наконец

$proc_status = Array ( [command] => /Server/texlive/2014/bin/win32/latex myfile.tex [pid] => 6420 [running] => 1 [signaled] => [stopped] => [exitcode] => -1 [termsig] => 0 [stopsig] => 0 )
end_proc_status = Array ( [command] => /Server/texlive/2014/bin/win32/latex myfile.tex [pid] => 6420 [running] => 1 [signaled] => [stopped] => [exitcode] => -1 [termsig] => 0 [stopsig] => 0 )

$end = 1428361897.55
$d = 10.2151138783
То есть висели в цикле ровно 10 сек, но так и не дождались в статусе процесса [running] => 0. Хотя очевидно, что latex отработал раньше. Файл dvi появляется мгновенно. За 10 сек можно не спеша в этом убедиться.

Когда запускаю dvipng, тогда все нормально работает. Никто не висит 10 секунд в цикле. [running] => 1 после нескольких итераций устанавливается в [running] => 0 и выходим из цикла по break.

Видимо, latex работатет в фоновом режиме и не умирает при завершении работы с tex-файлом. Как узнать, что он завершил работу?

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение07.04.2015, 03:08 


09/02/15
37
Попробуйте вот это делать сразу после proc_open, а не в конце:

Используется синтаксис PHP (brief)
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);


Т.е. latex вероятно определяет, что вы ему в stdin подсунули канал, и ждет, что вы ему туда пришлете документ на обработку, потому и не завершается. По-хорошему, 1-й и 2-й каналы надо не закрывать, а куда-то перенаправить - в /dev/null или еще куда-то, смотря какая ОС (и куда вам нужно).

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение07.04.2015, 04:43 


25/11/08
449
odelschwank, помогло. Большое спасибо!

Как же лучше определить эти параметры для proc_open.

Интересно, можно ли передать пустые массивы options и pipes. И куда будут направлены потоки, если передать пустые массивы?

Используется синтаксис PHP
$pipes = array();
$options = array();
$pid = proc_open( $cmd, $options, $pipes, $cwd );


Цитата:
По-хорошему, 1-й и 2-й каналы надо не закрывать, а куда-то перенаправить - в /dev/null или еще куда-то, смотря какая ОС (и куда вам нужно).

Допишу в конце команды >nul 2>&1.

 Профиль  
                  
 
 Re: Как ограничить время выполнения внешней программы?
Сообщение07.04.2015, 14:23 


25/11/08
449
Написал программку, которая спит заданное число сек. Протестировал на ней. Оказывается, proc_close не убивает процесс, а ждет окончания его работы. :?

Как узнать, latex и dvipng создают дочерние процессы, которые могут осиротеть?

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 9 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group