Intereting Posts
Получение того, что кажется ошибкой PHP4 на моем сервере PHP5 Созданный zip-файл содержит структуру папок с использованием команды tar в PHP Как использовать услугу переводчика внутри Entity? Как вы фильтруете результаты API WikiVoyage по городу? Изменение размера iframe в соответствии с высотой содержимого в нем. PHP, как установить цвет для определенных ключевых слов (текст) в скребковых данных Как сохранить изображение через захват в android на php? Открыть файл в VLC через HTML / JS или PHP FLASH as3 не может идентифицировать веб-адрес localhost php Получение необработанных SQL-запросов в CodeIgniter 1.7 Вывод скрипта после команды тайм-аута PHP / MySql Несколько выпадающих списков не нужны значения параметров, видимые в источнике страницы XML Дополнительный контент в конце документа Лог ошибок при попытке входа в систему с помощью Laravel 5.2 Свойства сайта кэша Smarty в базе данных

Почему бесконечно рекурсивная функция в PHP вызывает segfault?

Гипотетический вопрос для вас всех, чтобы пожевать …

Недавно я ответил на другой вопрос о SO, где скрипт PHP был прерван, и это напомнило мне то, о чем я всегда думал, поэтому давайте посмотрим, может ли кто-нибудь пролить свет на него.

Рассмотрим следующее:

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

Очевидно, что эта (бесполезная) функция бесконечно петляет. И, в конце концов, закончится память, потому что каждый вызов функции выполняется до того, как предыдущий закончен. Похоже на вилочную бомбу без разветвления.

Но … в конечном счете, на платформах POSIX сценарий будет умирать с SIGSEGV (он также умирает в Windows, но более изящно – насколько это может объяснить мои крайне ограниченные навыки отладки низкого уровня). Количество циклов варьируется в зависимости от конфигурации системы (память, выделенная для PHP, 32 бит / 64 бит и т. Д.) И ОС, но мой реальный вопрос: почему это происходит с segfault?

  • Это просто, как PHP обрабатывает ошибки «вне памяти»? Неужели должен быть более грациозный способ справиться с этим?
  • Это ошибка в движке Zend?
  • Есть ли способ, которым это можно контролировать или обрабатывать более изящно из PHP-скрипта?
  • Есть ли какой-либо параметр, который обычно контролирует максимальное количество рекурсивных вызовов, которые могут быть выполнены в функции?

Solutions Collecting From Web of "Почему бесконечно рекурсивная функция в PHP вызывает segfault?"

Если вы используете XDebug, существует максимальная глубина вложенности функции, которая контролируется установкой ini :

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

Выдает следующую ошибку:

Неустранимая ошибка: максимальный уровень вложенности функции «100» достигнут, прерывается!

Это ИМХО является гораздо лучшей альтернативой, чем segfault, поскольку он только убивает текущий скрипт, а не весь процесс.

Этот поток был включен в список внутренних дел несколько лет назад (2006). Его комментарии:

До сих пор никто не предлагал решение проблемы бесконечного цикла, которое бы удовлетворяло этим условиям:

  1. Никаких ложных срабатываний (т.е. хороший код всегда работает)
  2. Без замедления для выполнения
  3. Работает с любым размером стека

Таким образом, эта проблема остается нераскрытой.

Теперь, № 1, буквально невозможно решить из-за проблемы с остановкой . # 2 тривиально, если вы храните счетчик глубины стека (поскольку вы просто проверяете уровень инкрементного стека на push стека).

Наконец, проблема №3 гораздо сложнее решить. Учитывая, что некоторые операционные системы будут распределять пространство стека непересекающимся образом, реализовать его со 100% -ной точностью не будет, поскольку невозможно обеспечить переносимость размера стека или использования (для конкретной платформы это может быть возможно или даже легко, но не вообще).

Вместо этого PHP должен взять подсказку с XDebug и других языков (Python и т. Д.) И создать настраиваемый уровень вложенности (по умолчанию для Python установлено 1000) ….

Либо это, либо ловушку выделения памяти в стеке, чтобы проверить segfault перед тем, как это произойдет, и преобразовать это в RecursionLimitException чтобы вы могли восстановить ….

Я мог быть совершенно неправ, потому что мои тесты были довольно краткими. Похоже, что Php будет только отпадать, если закончится нехватка памяти (и, по-видимому, пытается получить доступ к недопустимому адресу). Если ограничение памяти установлено и достаточно низкое, вы получите ошибку из памяти заранее. В противном случае код seg неисправен и обрабатывается ОС.

Не могу сказать, является ли это ошибкой или нет, но сценарий, вероятно, не должен выходить из-под контроля, как это.

См. Сценарий ниже. Поведение практически идентично независимо от вариантов. Без ограничения памяти он также сильно замедляет мой компьютер до его уничтожения.

 <?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 возникнет, если переполнение стека. Обычно это обрабатывается внутри среды выполнения, и либо стек расширяется, либо сообщается о более элегантной ошибке, но могут быть реализации (и ситуации в других), где segfault просто позволяет подняться (или ускользнуть).