为什么PHP中的无限递归函数会导致错误?
一个假设性的问题,让你们大家仔细思考 我最近回答了另一个关于PHP脚本在哪里出错的问题,它让我想起了一些我一直想知道的事情,所以让我们看看是否有人可以解释一下 考虑以下几点:为什么PHP中的无限递归函数会导致错误?,php,segmentation-fault,Php,Segmentation Fault,一个假设性的问题,让你们大家仔细思考 我最近回答了另一个关于PHP脚本在哪里出错的问题,它让我想起了一些我一直想知道的事情,所以让我们看看是否有人可以解释一下 考虑以下几点: <?php function segfault ($i = 1) { echo "$i\n"; segfault($i + 1); } segfault(); ?> 显然,这个(无用的)函数无限循环。最终,将耗尽内存,因为对函数的每次调用都是在前一次调用完成之前执行的。有
<?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年)的内部清单上有一个。他的评论是:
到目前为止,还没有人提出解决这个无止境循环问题的方案
将满足以下条件:
或者在堆栈上捕获内存分配错误,以便在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();