【问题标题】:PHP: set_time_limit() seems to have no effect whatsoeverPHP:set_time_limit() 似乎没有任何效果
【发布时间】:2011-03-30 08:52:41
【问题描述】:

我正在尝试在普通的 PHP apache 脚本(不是 CLI)中使用 PHP 的 set_time_limit() 来限制其最大运行时间。它没有对系统调用或类似的东西做任何事情,我们没有使用安全模式(我们是服务器的所有者,但不是服务器的管理员)。这是 Linux (Ubuntu) 上的 PHP 5.2.4。

简单的测试用例:

ini_set('max_execution_time', 1);
set_time_limit(1);
$i=0;
while ($i++<100000000000) {} // or anything other arbitrary that takes time
die('Done');

预期结果:与超出脚本执行时间有关的事情

实际结果:打印完成。

我做错了什么吗? (顺便说一句,是的,脚本确实需要并且可以运行太长时间并且需要中止,不幸的是这不是可以避免的。这不是重点。)

【问题讨论】:

  • @StrikerNL Mark Ba​​ker(下)是对的。在 Windows 上,您将收到一个超出最大执行时间的错误(在 Windows 中为 cos sleep() 时间计数),但在 Linux 中则不会。
  • 是的,这是真的。请参阅修订版。对此感到抱歉。
  • @StrikerNL 也许你的服务器太强大了。脚本需要多长时间?尝试在其顶部和底部使用 microtime() 进行调试。
  • 添加零似乎并没有帮助。我想我遇到了某种错误,在我的本地开发服务器上,一切都按预期工作。也许我需要考虑将 PHP 升级到更新的版本,或者使用 register_tick_function() 或类似的东西来实现一些东西。
  • @StrikerNL 查看我的更新答案,这可能是您要查找的错误。

标签: php timeout


【解决方案1】:

出于安全和资源共享的原因,在某些系统上,ini_set 和类似功能被禁用。请与您的管理员联系,看看是否有此类限制。

【讨论】:

  • 不,我认为所有功能都已启用,应该没有限制。我们拥有服务器,但不管理它。
  • 无论您是否拥有服务器,都建议仔细检查。一个好的系统管理员会首先尽可能地锁紧盒子,然后在需要时减轻安全性。
【解决方案2】:

引用手册:

注意:

set_time_limit() 函数和 配置指令 max_execution_time 只影响 脚本本身的执行时间。 花在活动上的任何时间 发生在执行之外 脚本,例如系统调用使用 system(),流操作,数据库 查询等不包括在内时 确定的最长时间 脚本一直在运行。这不是 在测量的 Windows 上为 true 时间是真实的。

sleep() 是不影响脚本运行时间的函数之一

查看 MPHH 对手册 sleep() 页面的评论

注意:set_time_limit() 函数 和配置指令 max_execution_time 只影响 脚本本身的执行时间。 花在活动上的任何时间 发生在执行之外 脚本,例如系统调用使用 系统(),睡眠()函数, 不包括数据库查询等 当确定最长时间 脚本一直在运行。

【讨论】:

  • 是的,这是真的。请参阅修订版。对此感到抱歉。
【解决方案3】:

那是因为sleep这个功能被时间限制忽略了,sleep被激活时它会停止计时,当sleep被停用时它会重新开始计时。

【讨论】:

  • 是的,这是真的。请参阅修订版。
  • 抱歉,我尝试运行您修改后的脚本,但我没有看到“完成”被打印出来,我无法复制错误。我得把这个传下去,对不起。 :-/
【解决方案4】:

尝试让您的脚本实际执行某些操作,睡眠时间不计入执行时间,因为当脚本处于睡眠状态时它并没有执行。

编辑:查看此错误报告http://bugs.php.net/37306 最后一条评论是它已在“CVS HEAD、PHP_5_2 和 PHP_5_1”中修复。所以也许你的 PHP 版本有这个错误。也许您可以尝试更改max_input_time

【讨论】:

  • 是的,这是真的。请参阅修订版。对此感到抱歉。
【解决方案5】:

max_execution_time(...) 和 ini_set('max_execution_time',...) 都不会计算 sleep()、file_get_contents()、shell_exec()、mysql_query() 等的时间成本,所以我构建了以下函数 my_background_exec() 在后台/分离进程上运行静态方法,当时间到时,自动终止它:

my_exec.php:

<?php
function my_background_exec($function_name, $params, $str_requires, $timeout=600)
         {$map=array('"'=>'\"', '$'=>'\$', '`'=>'\`', '\\'=>'\\\\', '!'=>'\!');
          $str_requires=strtr($str_requires, $map);
          $path_run=dirname($_SERVER['SCRIPT_FILENAME']);
          $my_target_exec="/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}\\\$params=json_decode(file_get_contents('php://stdin'),true);call_user_func_array('{$function_name}', \\\$params);\"";
          $my_target_exec=strtr(strtr($my_target_exec, $map), $map);
          $my_background_exec="(/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}my_timeout_exec(\\\"{$my_target_exec}\\\", file_get_contents('php://stdin'), {$timeout});\" <&3 &) 3<&0";//php by default use "sh", and "sh" don't support "<&0"
          my_timeout_exec($my_background_exec, json_encode($params), 2);
         }

function my_timeout_exec($cmd, $stdin='', $timeout)
         {$start=time();
          $stdout='';
          $stderr='';
          //file_put_contents('debug.txt', time().':cmd:'.$cmd."\n", FILE_APPEND);
          //file_put_contents('debug.txt', time().':stdin:'.$stdin."\n", FILE_APPEND);

          $process=proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes);
          if (!is_resource($process))
             {return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
             }
          $status=proc_get_status($process);
          posix_setpgid($status['pid'], $status['pid']);    //seperate pgid(process group id) from parent's pgid

          stream_set_blocking($pipes[0], 0);
          stream_set_blocking($pipes[1], 0);
          stream_set_blocking($pipes[2], 0);
          fwrite($pipes[0], $stdin);
          fclose($pipes[0]);

          while (1)
                {$stdout.=stream_get_contents($pipes[1]);
                 $stderr.=stream_get_contents($pipes[2]);

                 if (time()-$start>$timeout)
                    {//proc_terminate($process, 9);    //only terminate subprocess, won't terminate sub-subprocess
                     posix_kill(-$status['pid'], 9);    //sends SIGKILL to all processes inside group(negative means GPID, all subprocesses share the top process group, except nested my_timeout_exec)
                     //file_put_contents('debug.txt', time().":kill group {$status['pid']}\n", FILE_APPEND);
                     return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
                    }

                 $status=proc_get_status($process);
                 //file_put_contents('debug.txt', time().':status:'.var_export($status, true)."\n";
                 if (!$status['running'])
                    {fclose($pipes[1]);
                     fclose($pipes[2]);
                     proc_close($process);
                     return $status['exitcode'];
                    }

                 usleep(100000); 
                }
         }
?>

test.php:

<?php
my_background_exec('A::jack', array('hello ', 'jack'), 'require "my_exec.php";require "a_class.php";', 8);
?>

a_class.php:

<?php
class A
{
    static function jack($a, $b)
           {sleep(4);
            file_put_contents('debug.txt', time().":A::jack:".$a.' '.$b."\n", FILE_APPEND);
            sleep(15);
           }
}
?>

【讨论】:

    【解决方案6】:

    通过检查 php.ini 中的 disable_functions 标志来检查您的 ini_set 是否有效。如果未禁用,请在您的 ini_set('max_execution_time', 1); 之后立即回显 phpinfo();以上。如果在本地列下更改了值,那么您知道 ini_set 有效。否则,您的脚本没有设置任何最大执行时间。

    【讨论】:

    • 是的,根据 phpinfo() 它已经改变了。然而它似乎并不尊重它。
    猜你喜欢
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 2015-12-05
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多