2011-09-06 12 views
31

आप सभी पर चबाने के लिए के लिए एक काल्पनिक सवाल ...PHP में असीमित रिकर्सिव फ़ंक्शन क्यों एक segfault का कारण बनता है?

मैं हाल ही में एसओ जहां एक PHP स्क्रिप्ट segfaulting गया था पर एक और सवाल का जवाब है, और यह मुझे कुछ मैं हमेशा आश्चर्य होता है की याद दिला दी है, तो देखते हैं अगर कोई शेड कर सकते हैं उस पर कोई प्रकाश।

निम्नलिखित पर विचार करें:

<?php 

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

    segfault(); 

?> 

जाहिर है, इस (बेकार) समारोह असीम लूप। और अंततः, स्मृति से बाहर हो जाएगा क्योंकि पिछले एक से पहले समारोह में प्रत्येक कॉल निष्पादित हो गया है। फोर्किंग के बिना एक कांटा बम की तरह सॉर्ट करें।

लेकिन ... आखिरकार, पॉज़िक्स प्लेटफॉर्म पर, स्क्रिप्ट एसआईजीएसईजीवी के साथ मर जाएगी (यह विंडोज़ पर भी मर जाती है, लेकिन अधिक सुन्दरता से - जहां तक ​​मेरा बेहद सीमित निम्न-स्तर डिबगिंग कौशल बता सकता है)। लूप की संख्या सिस्टम कॉन्फ़िगरेशन (PHP, 32 बिट/64 बिट इत्यादि को आवंटित स्मृति) और ओएस के आधार पर भिन्न होती है लेकिन मेरा असली सवाल यह है - यह एक सेगफॉल्ट के साथ क्यों होता है?

  • क्या यह आसानी से PHP "आउट-ऑफ-मेमोरी" त्रुटियों को कैसे संभालता है? निश्चित रूप से इसे संभालने का एक और अधिक शानदार तरीका होना चाहिए?
  • क्या यह ज़ेंड इंजन में एक बग है?
  • क्या कोई तरीका है जिसे इसे PHP स्क्रिप्ट के भीतर से अधिक सुन्दर तरीके से नियंत्रित या संभाला जा सकता है?
  • क्या ऐसी कोई सेटिंग है जो आमतौर पर उस कार्य में किए जा सकने वाले रिकर्सिव कॉल की अधिकतम संख्या को नियंत्रित करती है?
+0

PHP (5 iirc) के आधुनिक संस्करणों को रोकने के लिए रिकर्सन पर गहराई सीमा है इस तरह की चीज़। अगर यह segfaulting है, यह निश्चित रूप से एक बग है कि रिपोर्ट किया जाना चाहिए ... – ircmaxell

+7

[PHP के अनुसार] (https://bugs.php.net/bug.php?id=43187), यह इरादा व्यवहार है। – NullUserException

+0

यदि आप एक ऐसी भाषा की तलाश में हैं जिसमें रिकर्सन सीमा है, तो [पायथन] (http://docs.python.org/library/sys.html#sys.setrecursionlimit) – NullUserException

उत्तर

24

आप XDebug का उपयोग करते हैं, वहाँ एक अधिकतम समारोह नेस्टिंग गहराई है जो एक ini setting द्वारा नियंत्रित किया जाता है एक segfault, क्योंकि यह केवल वर्तमान स्क्रिप्ट को मारता है, पूरी प्रक्रिया नहीं।

this thread है जो कुछ साल पहले (2006) की आंतरिक सूची में था। उनकी यह टिप्पणी कर रहे हैं:

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% सटीकता के साथ कार्यान्वित करना संभव नहीं होगा, क्योंकि यह संभवतः स्टैक आकार या उपयोग को प्राप्त करना असंभव है (एक विशिष्ट प्लेटफॉर्म के लिए यह संभव हो सकता है या यहां तक ​​कि आसान, लेकिन सामान्य रूप से नहीं)।

इसके बजाय, पीएचपी XDebug और अन्य भाषाओं (अजगर, आदि) से संकेत ले जाना चाहिए और एक विन्यास नेस्टिंग स्तर बनाने (पायथन के set to 1000 डिफ़ॉल्ट रूप से है) ....

या तो उस, या जाल स्मृति आवंटन इससे पहले कि segfault की जांच करने के लिए स्टैक पर त्रुटियां हों और इसे RecursionLimitException में परिवर्तित करें ताकि आप पुनर्प्राप्त कर सकें ....

+0

एसआईजीएसईजीवी पकड़ो और अपवाद फेंक दें? – Demi

+0

जब मैं सेगमेंटेशन गलती का कारण ढूंढ रहा था, तब मुझे यह पोस्ट क्यों नहीं मिला। मैंने इस समस्या को स्टेजिंग सर्वर पर डिबग करने में घंटों बिताए। –

4

मैं इस बारे में पूरी तरह से गलत हो सकता था क्योंकि मेरा परीक्षण काफी संक्षिप्त था। ऐसा लगता है कि अगर PHP मेमोरी से बाहर हो जाता है (और संभावित रूप से किसी अमान्य पते तक पहुंचने का प्रयास करता है) तो PHP केवल गलती को रोक देगा। यदि स्मृति सीमा सेट है और पर्याप्त कम है, तो आप पहले से ही स्मृति त्रुटि से बाहर हो जाएगा। अन्यथा, कोड सेग दोष और ओएस द्वारा संभाला जाता है।

यह नहीं कह सकता कि यह एक बग है या नहीं, लेकिन स्क्रिप्ट को इस तरह के नियंत्रण से बाहर निकलने की अनुमति नहीं दी जानी चाहिए।

नीचे दी गई स्क्रिप्ट देखें। विकल्प विकल्पों के बावजूद व्यावहारिक रूप से समान है। स्मृति सीमा के बिना, यह मेरे कंप्यूटर को मारने से पहले गंभीर रूप से धीमा कर देता है।

<?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

वहां एक अच्छा प्रयोग है, समस्या का वर्णन करता है और अच्छी तरह से परिणाम देता है। आज सुबह कुछ और गुगलिंग के बाद, मैंने पाया [यह] (http://webcache.googleusercontent.com/search?q=cache:xGfXmRpzat4J:nicktelford.net/2010/06/18/handling-segmentation-faults-in-userland -php/+ हैंडलिंग + segfaults + में + उपयोगकर्तालैंड + php और cd = 1 और hl = en & ct = clnk और gl = uk) (Google कैश किया गया है क्योंकि साइट डाउन है) जो बताती है कि आप segfaults को जाल और संभाल सकते हैं - हालांकि ए) मुझे संदेह है कि यह काम करेगा आउट-ऑफ-मेमोरी स्थिति जिसके साथ हम काम कर रहे हैं और बी) मेरे पास पीसीएनटीएल एक्सटेंशन के साथ मशीन का परीक्षण करने के लिए स्थापित नहीं है। – DaveRandom

2

पीएचपी कार्यान्वयन के बारे में कुछ भी नहीं पता है, लेकिन यह ढेर के "टॉप" में आवंटित पृष्ठों को छोड़ने के लिए एक भाषा क्रम में असामान्य बात नहीं है ताकि एक segfault अगर ढेर अतिप्रवाह हो जाएगा। आमतौर पर इसे रनटाइम के अंदर संभाला जाता है और या तो स्टैक बढ़ाया जाता है या एक अधिक सुरुचिपूर्ण त्रुटि की सूचना दी जाती है, लेकिन वहां कार्यान्वयन (और दूसरों में स्थितियां) हो सकती हैं जहां सेगफॉल्ट को बढ़ने की अनुमति है (या भाग निकलता है)।

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

पैदा करता है निम्न त्रुटि:

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

यह IMHO की तुलना में कहीं बेहतर विकल्प है

+0

मैं इसके पीछे तर्क समझता हूं, लेकिन यह PHP स्क्रिप्ट को डीबग करने में कठोर बनाता है - मुझे नहीं पता कि segfault मेरी स्क्रिप्टिंग या ज़ेंड इंजन के कारण हुआ था या नहीं। सार्थक त्रुटि संदेश प्राप्त करना अच्छा लगेगा, लेकिन मैं स्वीकार करता हूं कि ऐसा कुछ भी नहीं है जो इसके बारे में व्यावहारिक रूप से किया जा सके। – DaveRandom

+0

मैं मानता हूं कि मैं आमतौर पर उस तरह की सतह के अपवादों को देने की परवाह नहीं करता हूं। लेकिन मैं उन परिस्थितियों को भी समझता हूं जो इस तरह के विकल्प को मजबूर कर सकते हैं - स्टैक ओवरफ्लो भाषा रनटाइम में संभालने के लिए सबसे कठिन चीजों में से एक है। –

संबंधित मुद्दे