Main > Linux | PHP > PHP, set_time_limit, exec

PHP, set_time_limit, exec

24.07.2013 4 comments » Views: 5,746

Ограничение времени работы внешних приложений

Всем привет! Давненько я не писал про интересные штуки при программировании на php. Сегодня я опишу один хак, который я нашел столкнувшись с проблемой запуска программ из php..

Итак, у нас есть задача: надо запускать некий внешний софт из php, после чего нужно обработать результат работы внешнего приложения. Делать это можно несколькими способами, например с помощью функции exec.

Например, так:

Получить результат работы в php можно так:

Создаем файл test.php с таким содержимым и запускаем его так из консоли:

Результатом работы будет следующий вывод:

Теперь поговорим о set_time_limit, данная функция, позволяет прервать выполнение скрипта, в случае если его время работы превысило время установленное в этой функции, самый простой пример на php может выглядеть так:

Вот какой будет результат:

Это отличный вариант, однако при вызове внешних команд, время не учитывается, модернизируем скрипт так и проверим:

Результат:

Как видите скрипт отработал 10 секунд и не был прерван, т.к. 10 секунд работала внешняя программа.

При разработке, я столкнулся со следующей проблемой: при запуске внешней программы, она иногда зависала по каким-то неведомым мне причинам. Т.е. управление не возвращалось в php скрипт.

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

Подобного поведения можно добиться например таким скриптом:

Скрипт будет работать, пока Вы не нажмете Ctrl+C (разумеется если вы его запустите из консоли)

Как же можно решить такую проблему?

Есть несколько вариантов:

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

Т.к. в моем случае, использование pcntl функций, значило бы переписывание части функционала, то меня такая перспектива не очень радовала. Поэтому я решил использовать второй вариант, а именно модифицировать команду запуска внешнего приложения таким образом, чтобы она автоматически завершалась по таймауту. Этот способ было очень просто реализовать, т.к. команды запуска были вынесены в конфигурационный файл.

Сделать это можно с помощью команды timeout вот так:

Формат команды timeout такой:

  • СИГНАЛ = сигнал который будет послан после времени указанного в ТАЙМАУТ_1. Обычно это SIGHUP или SIGTERM.
  • ТАЙМАУТ_1 = время после которого приложению будет послан сигнал SIGTERM, если не указан другой в пераметре -s. Время можно указывать с префикасми (s=секунда, m=минута, h=часы, d=дни), например: 20m = 20 минут
  • ТАЙМАУТ_2 = время после которого приложению будет послан сигнал SIGKILL, т.е. оно будет завершено. Начинает отсчитываться, после наступления ТАЙМАУТ_1. Т.е. время данное на завершение.
  • Команда возвращает код ошибки 124 в случае таймаута, либо код возвращения запущенной программы в случае если таймаута не было.

Результат:

Как видим через 10 секунд наша программа завершилась по таймауту, что нам и требовалось.

Есть еще один вариант, который я написал, до того, как нашел информацию о существовании команды timeout, вот он:

В принципе, он делает тоже самое, что и команда выше, только с более сложной записью.

Здесь мы делаем следующее:

  • Запускаем команду в фоне
  • Параллельно запоминаем PID запущенной команды в переменную
  • Далее ждем 5 секунд и отправляем приложению SIGTERM
  • После чего ждем еще 1 секунду и отправляем приложению SIGKILL

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

Если Вы знаете другие варианты решения такой проблемы, напишите в комментариях.

Author: | Rating: 4/5 | Tags: , ,

4 comments.

Write a comment
  1. Вадим Reply
    11.02.2014 в 6:25 pm
    Спасибо большое за #timeout -sСИГНАЛ -kТАЙМАУТ_2 ТАЙМАУТ_1 КОМАНДА!
    Очень долго искал, как и чем прерывать выполнение скрипта, зависающего по причине другого скрипта.
    • Vitaliy Orlov Reply
      11.02.2014 в 9:03 pm
      Пожалуйста, рад что помог! :)
  2. Junk Reply
    24.07.2013 в 8:42 pm
    Я понимаю, что от php никуда не деться, но после увиденного, мне еще больше захотелось выучить flash и action script.
    • Vitaliy Orlov Reply
      24.07.2013 в 9:04 pm
      А смысл? Flash-же на клиенте работает, а то, что описано в статье, на стороне сервера. Т.е. клиент это никак не увидит. Кроме того Adobe Flash умирает постепенно, лучше смотри в сторону HTML5.

Leave a Reply

Your email address will not be published. Required fields are marked *

Allowed HTML-tags: <a>, <code>, <i>, <em>, <strong>, <b>, <u>, <strike>