Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/265.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么PHP中的无限递归函数会导致错误?_Php_Segmentation Fault - Fatal编程技术网

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

为什么PHP中的无限递归函数会导致错误?,php,segmentation-fault,Php,Segmentation Fault,一个假设性的问题,让你们大家仔细思考 我最近回答了另一个关于PHP脚本在哪里出错的问题,它让我想起了一些我一直想知道的事情,所以让我们看看是否有人可以解释一下 考虑以下几点: <?php function segfault ($i = 1) { echo "$i\n"; segfault($i + 1); } segfault(); ?> 显然,这个(无用的)函数无限循环。最终,将耗尽内存,因为对函数的每次调用都是在前一次调用完成之前执行的。有

一个假设性的问题,让你们大家仔细思考

我最近回答了另一个关于PHP脚本在哪里出错的问题,它让我想起了一些我一直想知道的事情,所以让我们看看是否有人可以解释一下

考虑以下几点:

<?php

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

  segfault();

?>

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

但是。。。最终,在POSIX平台上,该脚本将随着SIGSEGV一起消亡(它也会在Windows上消亡,但更为优雅——就我极其有限的低级调试技能而言)。循环的数量取决于系统配置(分配给PHP的内存、32位/64位等)和操作系统,但我真正的问题是——为什么会发生segfault

  • 这就是PHP处理“内存不足”错误的简单方法吗?肯定有更优雅的方式来处理这件事
  • 这是Zend引擎中的错误吗
  • 有没有什么方法可以从PHP脚本中更优雅地控制或处理此问题
  • 是否有任何设置通常控制一个函数中可以进行的最大递归调用数

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

      不能说这是否是一个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;
      }
      ?>
      
      
      
      对PHP实现一无所知,但在语言运行时,在堆栈的“顶部”保留未分配的页面,以便在堆栈溢出时发生segfault,这种情况并不少见。通常这是在运行时内部处理的,堆栈被扩展或报告了一个更优雅的错误,但也可能有一些实现(以及其他情况)只允许SEGFULT上升(或转义)。

      如果使用XDebug,则有一个最大函数嵌套深度,该深度由以下参数控制:

      产生以下错误:

      致命错误:已达到最大函数嵌套级别“100”,正在中止

      这个IMHO比segfault好得多,因为它只杀死当前脚本,而不是整个过程

      在几年前(2006年)的内部清单上有一个。他的评论是:

      到目前为止,还没有人提出解决这个无止境循环问题的方案 将满足以下条件:

    • 无误报(即,好代码始终有效)
    • 执行不放慢
    • 适用于任何堆栈大小
    • 因此,这个问题仍然没有得到解决

      现在,#1实际上是不可能解决的,因为#如果保留堆栈深度计数器(因为您只是在堆栈推送上检查递增的堆栈级别),则2是微不足道的

      最后,3是一个更难解决的问题。考虑到某些操作系统将以非连续的方式分配堆栈空间,不可能100%准确地实现,因为不可能以可移植的方式获得堆栈大小或使用情况(对于特定的平台,这可能是可能的,甚至是容易的,但通常不可能)

      相反,PHP应该接受XDebug和其他语言(Python等)的提示,并创建一个可配置的嵌套级别(Python的默认值为1000)


      或者在堆栈上捕获内存分配错误,以便在segfault发生之前检查它,并将其转换为
      RecursionLimitException
      ,以便您能够恢复…

      ,这是预期的行为。@NullUserException很有趣,我搜索了PHP错误,但没有发现。。。他们说这是一个已知的递归限制,但却没有给出该限制的约束条件,或者提供任何控制它的方法,这似乎很奇怪。正如该bug的报告者所说,这只会在编写bug代码的情况下造成问题,但如果知道边界在哪里就好了。我希望所有爆炸的函数都将自己重命名为
      segfult
      ——这肯定会节省办公室的一些长夜@Lawrence Cherone有些情况下,代码并不打算耗尽堆栈,但确实会耗尽堆栈(比如说,一个完美的递归算法遇到了退化的情况;您知道一个正常的“bug”)。PHP只是有一个不可接受的“解决方案”,IMOHO。(Ruby、Perl和Python——3个动态竞争对手施加了更理智的、但有点武断的限制。)@Lawrence将segfault称为“完美的错误代码”有点太过分了,是吗?一个很好的实验,很好地说明了问题和结果。今天早上在谷歌上进一步搜索之后,我发现(谷歌缓存了,因为网站关闭了)这表明您可以捕获和处理SEGFULTS-尽管a)我怀疑它在我们正在处理的内存不足情况下是否能工作,b)我没有安装PCNTL扩展的机器来测试它。我有点理解这背后的原因,但这确实使PHP脚本更难调试——我不知道SEGFULT是由我的脚本还是Zend引擎引起的。如果能得到一条有意义的错误消息就好了,但我承认实际上对此无能为力。我同意,我一般不喜欢让这类异常浮出水面。但我也理解可能迫使做出这种选择的环境——堆栈溢出是最困难的事情之一
      $foo = function() use (&$foo) { 
          $foo();
      };
      $foo();