为什么PHP中的无限递归函数会导致段错误?

时间:2011-09-06 23:43:30

标签: php segmentation-fault

一个假设的问题,你们都要咀嚼......

我最近在SO上回答了另一个问题,其中一个PHP脚本是segfaulting,它让我想起了我一直想知道的东西,所以让我们看看是否有人可以对它有所了解。

请考虑以下事项:

<?php

  function segfault ($i = 1) {
    echo "$i\n";
    segfault($i + 1);
  }

  segfault();

?>

显然,这个(无用的)函数无限循环。最终,由于每个对函数的调用都是在前一个调用完成之前执行的,因此会耗尽内存。有点像没有分叉的叉炸弹。

但是......最终,在POSIX平台上,脚本会死于SIGSEGV(它也会死在Windows上,但更优雅 - 只要我极其有限的低级调试技能可以说明)。循环次数取决于系统配置(分配给PHP的内存,32位/ 64位等)和操作系统,但我真正的问题是 - 为什么会发生段错误?

  • 这就是PHP处理“内存不足”错误的方式吗?当然必须有更优雅的方式处理这个问题吗?
  • 这是Zend引擎中的错误吗?
  • 有没有什么方法可以在PHP脚本中更优雅地控制或处理它?<​​/ li>
  • 是否有任何设置通常控制可以在函数中进行的最大递归调用次数?

3 个答案:

答案 0 :(得分:24)

答案 1 :(得分:4)

我可能完全错了,因为我的测试相当简短。似乎Php只会在内存不足时出错(并且可能会尝试访问无效的地址)。如果内存限制设置且足够低,则会预先出现内存不足错误。否则,代码段出错并由操作系统处理。

不能说这是否是一个bug,但是可能不允许脚本失控。

请参阅下面的脚本。无论选项如何,行为几乎相同。没有内存限制,它也会在我的计算机被杀之前严重减慢它。

<?php
$opts = getopt('ilrv');
$type = null;
//iterative
if (isset($opts['i'])) {
   $type = 'i';
}
//recursive
else if (isset($opts['r'])) {
   $type = 'r';
}
if (isset($opts['i']) && isset($opts['r'])) {
}

if (isset($opts['l'])) {
   ini_set('memory_limit', '64M');
}

define('VERBOSE', isset($opts['v']));

function print_memory_usage() {
   if (VERBOSE) {
      echo memory_get_usage() . "\n";
   }
}

switch ($type) {
   case 'r':
      function segf() {
         print_memory_usage();
         segf();
      }
      segf();
   break;
   case 'i':
      $a = array();
      for ($x = 0; $x >= 0; $x++) {
         print_memory_usage();
         $a[] = $x;
      }
   break;
   default:
      die("Usage: " . __FILE__ . " <-i-or--r> [-l]\n");
   break;
}
?>

答案 2 :(得分:2)

对PHP实现一无所知,但在语言运行时中将页面未分配到堆栈的“顶部”并不常见,以便在堆栈溢出时发生段错误。通常这是在运行时内部处理的,并且堆栈被扩展或报告更优雅的错误,但是可能存在实现(以及其他情况)其中segfault被允许上升(或逃逸)。