2011-09-06 23 views
31

私は最近、PHPスクリプトがセグメンテーションフォルトたSO場所に別の質問に答え、それは私がいつも疑問に思っている何かを思い出させてくれましたので、誰でも当てることができるかどうかを見てみましょうPHPで無限再帰関数がsegfaultを引き起こすのはなぜですか?

あなたに噛むためにすべてのための仮定の質問...その上の光。

は、次の点を考慮

<?php 

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

    segfault(); 

?> 

明らかに、この(役に立たない)関数が無限ループします。最終的には、前の関数が終了する前に関数の各呼び出しが実行されるため、メモリが不足します。フォークのないフォーク爆弾のようなもの。

しかし最終的には、POSIXプラットフォームでは、スクリプトはSIGSEGVで消滅します(これはWindowsでも死亡しますが、より上品に - 非常に限られた低レベルのデバッグスキルが示す限り)。ループの数は、システム構成(PHP、32ビット/ 64ビットなどに割り当てられたメモリ)やOSによって異なりますが、私の本当の疑問は、なぜセグメンテーションで起こるのでしょうか?

  • これはPHPが「メモリ不足」エラーを処理する方法ですか?確かにこれを処理するより優雅な方法が必要でしょうか?
  • これはZendエンジンのバグですか?
  • これは、PHPスクリプト内からより適切に制御または処理できる方法はありますか?
  • 関数内で実行できる再帰呼び出しの最大数を一般的に制御する設定はありますか?
+0

現バージョンのphp(5 iirc)はこれを防ぐために再帰に深さ制限がありますものの種類。 segfaultingの場合は、間違いなく報告すべきバグです... – ircmaxell

+7

[PHPによる](https://bugs.php.net/bug.php?id=43187)、これは意図した動作です。 – NullUserException

+0

再帰制限を持つ言語を探している場合は、[Python](http://docs.python.org/library/sys.html#sys.setrecursionlimit)を試してください。 – NullUserException

答えて

24

あなたはXDebugのを使用している場合は、ini settingによって制御される最大関数のネストの深さがあります:

$foo = function() use (&$foo) { 
    $foo(); 
}; 
$foo(); 

は、次のエラーを生成します:

Fatal error: Maximum function nesting level of '100' reached, aborting!

これ私見ではよりもはるかに良い代替手段ですこれはプロセス全体ではなく、現在のスクリプトを殺すだけなので、segfaultです。

数年前(2006年)の内部リストにはthis threadがあります。彼のコメントは以下のとおりです。

So far nobody had proposed a solution for endless loop problem that would satisfy these conditions:

  1. No false positives (i.e. good code always works)
  2. No slowdown for execution
  3. Works with any stack size

Thus, this problem remains unsloved.

今すぐ、#1はhalting problemのために解決するために、文字通り不可能です。 #2はスタック深さのカウンタを保持している場合(スタックプッシュで増分されたスタックレベルをチェックしているので)、自明です。

最後に、#3は解決するのがもっと難しい問題です。いくつかのオペレーティングシステムがスタック領域を不連続に割り当てることを考えると、スタックサイズや使用量を移植可能にすることは不可能であるため、100%の精度で実装することはできません(特定のプラットフォームでは可能かもしれません。簡単ですが、一般的ではありません)。

代わりに、PHPがあること、またはトラップメモリ​​割り当てのどちらかのXDebugと他の言語(Pythonの、等)からヒントを取り、設定可能なネストレベルを(Pythonのは、デフォルトではset to 1000である)を作る....

必要がありますスタック上のエラーが起こる前にsegfaultをチェックし、それを復旧できるようにRecursionLimitExceptionに変換してください。

+0

SIGSEGVをキャッチして例外をスローしますか? – Demi

+0

セグメンテーションフォールトの原因を探していた前に、なぜ私はこのポストを見つけられなかったのですか?ステージングサーバーでこの問題をデバッグする時間を費やしました。 –

4

私のテストはかなり短かったので、これは完全に間違っている可能性があります。それはメモリが不足している(とおそらく無効なアドレスにアクセスしようとする)場合、PHPは障害をsegするだけと思われる。メモリ制限が設定されていて、十分に低い場合は、あらかじめメモリ不足エラーが発生します。それ以外の場合、コードはエラーを起こし、OSによって処理されます。

これはバグかどうかは言えませんが、スクリプトはこのような制御から抜けることはおそらく許されません。

以下のスクリプトを参照してください。行動はオプションにかかわらず事実上同じです。メモリの制限がないと、コンピュータが死ぬ前にコンピュータの動作が遅くなります。

<?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; 
} 
?> 
+0

そこに実験の良いビットは、問題と結果をうまく示しています。今朝のある種のグーグルで、私は[this](http://webcache.googleusercontent.com/search?q=cache:xGfXmRpzat4J:nicktelford.net/2010/06/18/handling-segmentation-faults-in-userland)が見つかりました。あなたがセグメンテーションをトラップして処理できることを示唆している - (a)私はそれがうまくいくとは思うが、-php/+ handling + segfaults + in + userland + php&cd = 1&hl = en&ct = clnk&gl = uk)私たちが扱っているメモリ不足の状況、およびb)テストするためにPCNTLエクステンションがインストールされたマシンがありません。 – DaveRandom

2

PHPの実装について何も知らないが、それはスタックオーバーフロー場合セグメンテーション違反が発生するように、スタックの「一番上」に未割り当てのページを残すために言語ランタイムでは珍しくありません。通常、これはランタイムの内部で処理され、スタックが拡張されるか、よりエレガントなエラーが報告されますが、segfaultが単純に立ち上がる(またはエスケープする)実装が存在する可能性があります。

+0

私はこの背後にある理由を理解していますが、PHPスクリプトをデバッグするのが難しくなっています。スクリプトやZendエンジンによってsegfaultが発生したのかどうかわかりません。意味のあるエラーメッセージを得ることはうれしいことですが、実際にこれについて行うことはできません。 – DaveRandom

+0

私は、一般的に、その並べ替え面の例外を許可しないことに同意します。しかし、私はまた、そのような選択を強制することができる状況を理解しています - スタックオーバーフローは、言語ランタイムで処理するのが最も難しいことの1つです。 –

関連する問題