Как решить set_time_limit, не влияя на PHP-CLI?
#!/usr/bin/php -q <?php set_time_limit(2); sleep(5); // actually, exec() call that takes > 2 seconds echo "it didn't work again";
Решением здесь является использование pcntl_fork()
. Боюсь, что это только POSIX. PHP необходимо скомпилировать с помощью --enable-pcntl
и not --disable-posix
. Ваш код должен выглядеть примерно так:
<?php function done($signo) { // You can do any on-success clean-up here. die('Success!'); } $pid = pcntl_fork(); if (-1 == $pid) { die('Failed! Unable to fork.'); } elseif ($pid > 0) { pcntl_signal(SIGALRM, 'done'); sleep($timeout); posix_kill($pid, SIGKILL); // You can do any on-failure clean-up here. die('Failed! Process timed out and was killed.'); } else { // You can perform whatever time-limited operation you want here. exec($cmd); posix_kill(posix_getppid(), SIGALRM); } ?>
В основном, это делает fork новый процесс, который запускает блок else { ... }
. При успешном завершении мы отправляем сигнал тревоги родительскому процессу, который запускает блок elseif ($pid > 0) { ... }
. Этот блок имеет зарегистрированный обработчик сигнала для сигнала тревоги done()
обратный вызов done()
), который успешно завершается. Пока это не будет получено, родительский процесс будет спать. Когда тайм-аут sleep()
завершен, он отправляет SIGKILL
в дочерний процесс (предположительно висящий).
Предел max_execution_time
, который задает set_time_limit
, подсчитывает (по крайней мере, в Linux) время, затрачиваемое процессом PHP, во время работы.
Цитирование страницы руководства set_time_limit()
:
Примечание. Функция
set_time_limit()
и директива конфигурацииmax_execution_time
влияют только на время выполнения самого скрипта .
Любое время, затрачиваемое на активность, которое происходит вне выполнения сценария, например системные вызовы с использованиемsystem()
, потоковые операции, запросы к базе данных и т. Д., Не включается при определении максимального времени выполнения сценария.
Это не относится к Windows, где измеренное время реально.
Когда вы используете sleep()
, ваш PHP-скрипт не работает: он просто ждет … Итак, 5 секунд, которые вы ожидаете, не учитываются лимитом max_execution_time
.
Не могли бы вы попробовать запустить php через exec и использовать команду «timeout»? (http://packages.ubuntu.com/lucid/timeout)
use: команда timeout [-signal] time.
Я понимаю, что скрипт висит в цикле. Почему бы просто не $STARTUP_TIME=time()
начало скрипта, а затем продолжить проверку в цикле, если скрипт перестанет работать?
Очень хаки, но, поскольку я часто использую скрипты cli, я допускаю позорное совпадение с подобным взломом в прошлом. Требуется 2 скрипта.
Ваш первый cron-скрипт (тот, который зависает) регистрирует свое время начала и processid в плоском файле -> функция getmypid будет для вас другом. Второй скрипт, выполняемый cron каждые (x) минуты, проверяет файл flatfile, убивает все, что прошло заодно, время выполнения, очищает файл flatfile и даже журналы в другом файле, если вы хотите.
Удачи 😉
ОБНОВИТЬ:
Вспомнил этот вопрос и вернулся, чтобы опубликовать это. В то время как рывком по счету Github Себастьяна Бергмана я наткнулся на php-invoker . По словам автора:
Класс полезности для вызова PHP-вызовов с таймаутом.
Несмотря на то, что текущий ответ очень хорош для не-портативного использования на базе Linux, если требуется кросс-платформенное решение, это решение должно быть совместимым с окнами, для тех бедных душ, которые работают на ветровом стекле. Г-н Бергманн, будучи богом PHP, я полностью ручаюсь за качество сценария.
Я заметил этот комментарий в руководстве PHP:
Обратите внимание, что в Linux время сна игнорируется, но под Windows оно считается временем выполнения.
Насколько я понимаю, сон реализуется как системный вызов и поэтому игнорируется PHP, как описано в руководстве .
там вместо exec () работает exec (). и некоторые exec () вещи зависают бесконечно. Я также пытаюсь убить его из exec (на linux shell), например exec (kill_after15secs myscript.sh), но я не могу найти, что либо
Теперь я вижу реальный вопрос. Предполагая, что вы работаете в среде Lunix / Unix, вы можете разработать решение для этих строк:
<?php $pid = system( 'sh test.sh >test-out.txt 2>&1 & echo $!' ); ?>
Угадайте, что это зафиксировало идентификатор процесса начатого вами процесса. Вы можете записать его в базу данных или текстовый файл вместе с меткой времени. В другом скрипте cron, который запускается, скажем, каждые 5 минут, извлекает все идентификаторы процессов, которые были созданы 5 минут назад, и проверьте, все ли они запущены:
<?php $status = system( 'ps ' . $pid ); ?>
Если процесс все еще запущен, вы получаете две строки вывода:
PID TTY STAT TIME COMMAND 2044 ? S 0:29 sh test.sh
Если это так, прекратите его следующим образом:
<?php system( 'kill ' . $pid ); ?>
Я написал статью о создании фоновых процессов в PHP (и затем охоту на них). Оттуда были скопированы все примеры.
Все точки, соединенные вместе:
<?php $pid = system( 'sh test.sh >test-out.txt 2>&1 & echo $!' ); $timer = 300; while( --$timer ) { sleep(1); $status = system( 'ps ' . $pid ); $status = explode( "\n", $status, 2 ); // I am not sure, please experiment a bit here if ( count( $status ) == 1 || trim( $status[ 1 ] ) == '' ) { die( 'spawned process ended successfully' ); } } system( 'kill ' . $pid ); die( 'spawned process was terminated' ); ?>