2011-09-06 38 views
31

Một câu hỏi giả định cho tất cả các bạn nhai ...Tại sao một hàm đệ quy vô hạn trong PHP lại gây ra sự phân đoạn?

Gần đây tôi đã trả lời một câu hỏi khác về kịch bản PHP, và nó nhắc tôi về điều mà tôi luôn thắc mắc. bất kỳ ánh sáng nào trên đó.

xem xét như sau:

<?php 

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

    segfault(); 

?> 

Rõ ràng, (vô dụng) chức năng này lặp vô hạn. Và cuối cùng, sẽ hết bộ nhớ vì mỗi cuộc gọi đến hàm thực thi trước khi hàm trước kết thúc. Sắp xếp giống như một quả bom ngã ba mà không có tiếng còi. Tuy nhiên, cuối cùng, trên nền tảng POSIX, kịch bản sẽ chết với SIGSEGV (nó cũng chết trên Windows, nhưng duyên dáng hơn - cho đến khi các kỹ năng gỡ lỗi cấp thấp cực kỳ hạn chế của tôi có thể cho biết). Số vòng lặp thay đổi tùy thuộc vào cấu hình hệ thống (bộ nhớ được cấp phát cho PHP, 32bit/64bit, v.v.) và hệ điều hành nhưng câu hỏi thực sự của tôi là - tại sao nó lại xảy ra với segfault?

  • Đây có phải là cách PHP xử lý các lỗi "hết bộ nhớ" không? Chắc chắn phải có một cách duyên dáng hơn để xử lý điều này?
  • Đây có phải là lỗi trong công cụ Zend không?
  • Có cách nào điều này có thể được kiểm soát hoặc xử lý một cách duyên dáng hơn từ bên trong tập lệnh PHP không?
  • Có bất kỳ cài đặt nào thường kiểm soát số lượng cuộc gọi đệ quy tối đa có thể được thực hiện trong một hàm không?
+0

Phiên bản hiện tại của php (5 iirc) có giới hạn chiều sâu đệ quy để ngăn chặn điều này loại điều. Nếu nó là segfaulting, nó chắc chắn là một lỗi mà nên được báo cáo ... – ircmaxell

+7

[Theo PHP] (https://bugs.php.net/bug.php?id=43187), đây là hành vi dự định. – NullUserException

+0

Nếu bạn đang tìm kiếm một ngôn ngữ có giới hạn đệ quy, hãy thử [Python] (http://docs.python.org/library/sys.html#sys.setrecursionlimit) – NullUserException

Trả lời

24

Nếu bạn sử dụng XDebug, có một chức năng làm tổ sâu tối đa mà được điều khiển bởi một ini setting:

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

sản xuất các lỗi sau:

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

IMHO Đây là một lựa chọn tốt hơn nhiều so một segfault, vì nó chỉ giết chết kịch bản hiện tại, không phải toàn bộ quá trình.

this thread nằm trong danh sách nội bộ vài năm trước (2006). Ý kiến ​​của anh ấy là:

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.

Bây giờ, # 1 hoàn toàn không thể giải quyết được do halting problem. # 2 là tầm thường nếu bạn giữ một bộ đếm chiều sâu ngăn xếp (vì bạn chỉ kiểm tra mức tăng stack trên stack stack).

Cuối cùng, # 3 Có vấn đề khó giải quyết hơn nhiều. Xem xét một số hệ điều hành sẽ phân bổ không gian ngăn xếp theo cách không tiếp giáp, sẽ không thể thực hiện với độ chính xác 100%, vì không thể có được kích thước hoặc cách sử dụng stack (đối với một nền tảng cụ thể có thể có hoặc thậm chí dễ dàng, nhưng không nói chung).

Thay vào đó, PHP nên lấy gợi ý từ XDebug và các ngôn ngữ khác (Python, vv) và thực hiện một mức độ làm tổ cấu hình (Python là set to 1000 theo mặc định) ....

Dù rằng, hoặc cấp phát bộ nhớ bẫy lỗi trên ngăn xếp để kiểm tra segfault trước khi nó xảy ra và chuyển đổi thành một RecursionLimitException để bạn có thể khôi phục ....

+0

Nắm bắt SIGSEGV và ném ngoại lệ? – Demi

+0

Tại sao tôi không tìm thấy bài đăng này trước đây, khi tôi đang tìm nguyên nhân của lỗi phân đoạn. Tôi đã dành hàng giờ gỡ lỗi vấn đề này trên một máy chủ dàn dựng. –

4

Tôi có thể hoàn toàn sai về điều này vì thử nghiệm của tôi khá ngắn gọn. Có vẻ như Php sẽ chỉ seg lỗi nếu nó hết bộ nhớ (và có lẽ cố gắng truy cập một địa chỉ không hợp lệ). Nếu giới hạn bộ nhớ được đặt và đủ thấp, bạn sẽ bị lỗi bộ nhớ trước. Nếu không, mã seg lỗi và được xử lý bởi hệ điều hành.

Không thể nói đây có phải là lỗi hay không, nhưng tập lệnh có thể không được phép vượt khỏi tầm kiểm soát như thế này.

Xem tập lệnh bên dưới. Hành vi là thực tế giống nhau bất kể tùy chọn. Nếu không có giới hạn bộ nhớ, nó cũng làm chậm máy tính của tôi xuống nghiêm trọng trước khi nó bị giết.

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

Một chút thử nghiệm ở đó, minh họa cho vấn đề và kết quả tốt nhất. Sau khi một số Googling hơn nữa sáng nay, tôi thấy [this] (http://webcache.googleusercontent.com/search?q=cache:xGfXmRpzat4J:nicktelford.net/2010/06/18/handling-segmentation-faults-in-userland -p/+ xử lý + segfaults + trong + userland + php & cd = 1 & hl = vi & ct = clnk & gl = uk) (Google được lưu vào bộ nhớ cache vì trang web bị lỗi) cho thấy bạn có thể bẫy và xử lý các segfaults - mặc dù a) Tôi nghi ngờ nó sẽ hoạt động tình trạng hết bộ nhớ chúng ta đang xử lý và b) Tôi không có một máy tính có phần mở rộng PCNTL được cài đặt để kiểm tra nó. – DaveRandom

2

Không biết gì về triển khai PHP, nhưng không có gì lạ trong thời gian chạy ngôn ngữ để các trang không được phân bổ ở "đầu" của ngăn xếp để phân đoạn xảy ra nếu ngăn xếp tràn. Thông thường, điều này được xử lý bên trong thời gian chạy và ngăn xếp được mở rộng hoặc lỗi thanh lịch hơn được báo cáo, nhưng có thể có các triển khai (và tình huống ở những người khác), nơi segfault đơn giản được phép tăng (hoặc thoát).

+0

Tôi sắp hiểu lý do đằng sau điều này, nhưng nó làm cho kịch bản PHP khó gỡ lỗi hơn - tôi không thể biết liệu segfault có phải do kịch bản của tôi hay công cụ Zend gây ra hay không. Sẽ rất tuyệt khi nhận được một thông báo lỗi có ý nghĩa, nhưng tôi chấp nhận không có gì thực tế có thể được thực hiện về điều này. – DaveRandom

+0

Tôi đồng ý rằng tôi thường không quan tâm đến việc cho phép ngoại lệ của bề mặt sắp xếp đó. Nhưng tôi cũng hiểu những hoàn cảnh có thể ép buộc một sự lựa chọn như vậy - tràn bộ đệm là một trong những điều khó khăn nhất để xử lý trong một thời gian chạy ngôn ngữ. –

Các vấn đề liên quan