Как ограничить время выполнения внешней программы?

Все о скриптах

Модераторы: Art.i, vasya

Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 8:28 am

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

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

Если нет, то как можно организовать убийство процесса в PHP? Допустим, запускаем процесс через popen(). Как отследить время? Sleep() не подходит, т.к. при успешном завершении, необходимо тут же продолжить, а не спать.
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение Art.i » Пн апр 06, 2015 10:46 am

Если у вашего скрипта уникальное имя(его можно увидеть если на сервере запустить top).
То можно к вашей команде в конце просто добавить
Код: выделить все
&& sleep 10 ; killall 'имя процесса'


Например:
Код: выделить все
bash /script  && sleep 10 ; killall script

В sleep указывается время в секундах. В данном случае, команда завершится через 10 секунд.
Art.i
Support team
 
Сообщений: 719
Зарегистрирован: Ср сен 25, 2013 2:07 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение pepper » Пн апр 06, 2015 11:27 am

Если sleep не подходит, как вы написали, то лучше тут отладить скрипт, чтобы не происходило непонятного "иногда она зависает".
pepper
Support team
 
Сообщений: 551
Зарегистрирован: Пн окт 07, 2013 4:06 am

Re: Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 4:31 pm

В sleep указывается время в секундах. В данном случае, команда завершится через 10 секунд.

А если лимит у меня 20 сек, а процесс завершится за 0,2 сек, то будем зря ждать. Хотелось бы задать именно предельное время с возможностью завершиться раньше.

Если sleep не подходит, как вы написали, то лучше тут отладить скрипт, чтобы не происходило непонятного "иногда она зависает".
Это не мой скрипт, а внешняя программа dvipng.

А так будет нормально или такой частный цикл будет слишком нагружать?
Код: выделить все
   
<?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);
    ?>
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение swg » Пн апр 06, 2015 5:27 pm

ijkl писал(а):В PHP запускаю внешнюю программу через shell_exec(). Но иногда она зависает. Как из скрипта запускать с возможностью прервать выполнение по истечении определенного времени?
shell_exec забыть, вариант один - proc_open
Нужен ли цикл проверки? Можно ли скинуть контент пользователю и завершить соединение (скрипт всё равно продолжит работу и прервется через 10 секунд)? Т.е. не заставлять браузер ждать. Пользователь потом будет сам "спрашивать" через ajax, доступен ли результат. Если 15 секунд результата нет - вывести "Упс, пробуй еще раз".
swg
флудит форум
 
Сообщений: 2386
Зарегистрирован: Сб окт 07, 2006 9:09 am
Откуда: NNov

Re: Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 7:31 pm

swg писал(а):Нужен ли цикл проверки? Можно ли скинуть контент пользователю и завершить соединение (скрипт всё равно продолжит работу и прервется через 10 секунд)? Т.е. не заставлять браузер ждать. Пользователь потом будет сам "спрашивать" через ajax, доступен ли результат. Если 15 секунд результата нет - вывести "Упс, пробуй еще раз".
Не, результат работы внешней программы должен еще оцениваться и обрабатываться в самом php-скрипте. Без цикла нельзя? Как вообще оптимально сделать прерывание процесса по времени?
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение swg » Пн апр 06, 2015 7:42 pm

Ну так пускай оценивается и обрабатывается, я лишь написал, что пользователя нужно освободить от ожидания, если таковое предполагается.
2 независимых процесса: 1() обрабатывает и записывает результат, например, в БД. (2) интерактивный, запрашивает результат (ajax запрос, например, каждые пару секунд), если его нет больше положенного, то говорит о неудаче.
Схема (1) примерно такая
Код: выделить все
запустили proc_open, долгий процесс
запомнили время
сделали что-то независимое от долгого процесса
скинули пользователя, всё браузер не ждет, а рисует картинку "ждите..."
посчитали потраченное время
ждем от потраченное время до 10 секунд, проверяя статус каждую секунду (10 мсек - слишком часто, proc_get_status съест все CPU)
записываем результат куда-нибудь в БД

Пока не уверен, что есть возможность установить callback такой в php "из коробки", либо еще не знаю об этом.
swg
флудит форум
 
Сообщений: 2386
Зарегистрирован: Сб окт 07, 2006 9:09 am
Откуда: NNov

Re: Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 9:56 pm

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

Будет ли работать так? Не произойдет ли убийство раньше времени?

Код: выделить все
$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);
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение swg » Пн апр 06, 2015 10:07 pm

Никак не определяется. Я не знаю, но может latex-у в консоли надо что-то ввести (ну или с какими-то другими ключами запускать)? Т.е. с ним общаться можно fwrite($pipes[0],$...); после proc_open.
Как у вас, со stream_select - в общем случае не будет работать как задумано. Например, любой байт в поток вывода и она прервётся; а может процесс еще должен работать.
swg
флудит форум
 
Сообщений: 2386
Зарегистрирован: Сб окт 07, 2006 9:09 am
Откуда: NNov

Re: Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 10:26 pm

swg писал(а):Я не знаю, но может latex-у в консоли надо что-то ввести (ну или с какими-то другими ключами запускать)?

Та же самая команда $cmd через shell_exec выполняется и нормально завершается. Значит главный вопрос в том, как latex сообщает shell_exec, что пора выходить. Вернее, как shell_exec узнает, что пора завершаться.

swg писал(а):Как у вас, со stream_select - в общем случае не будет работать как задумано. Например, любой байт в поток вывода и она прервётся; а может процесс еще должен работать.
Да, я об этом же подумал.

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

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

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


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

Или же shell_exec выходит, отдает выходной поток вызывающему скрипту, но при этом не убивает программу, давая возможность ей продолжать вывод?
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm

Re: Как ограничить время выполнения внешней программы?

Сообщение swg » Пн апр 06, 2015 10:40 pm

Не работал со shell_exec, не предназначена она для этого. Есть "бесконечные" команды, типа top. Вот её в shell_exec выполните, она завершается и передаёт управление скрипту, что в реальной консоли не должно происходить, пока сам из top не выйдешь.
swg
флудит форум
 
Сообщений: 2386
Зарегистрирован: Сб окт 07, 2006 9:09 am
Откуда: NNov

Re: Как ограничить время выполнения внешней программы?

Сообщение ijkl » Пн апр 06, 2015 11:13 pm

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

$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
ijkl
 
Сообщений: 215
Зарегистрирован: Чт окт 03, 2013 5:42 pm


Вернуться в CGI, Perl, PHP

Кто сейчас на форуме

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