Страницы

Поиск по вопросам

пятница, 31 января 2020 г.

Переполнение системного стека (не структуры данных)

#стек


Как стек знает, что он переполнен?
Когда программа проверяет, не переполнен ли стек?
Что происходит при переполнении стека?
    


Ответы

Ответ 1



Как стек знает, что он переполнен? Об этом знает операционная система. Она, в свою очередь, узнаёт об этом при попытке программы прочитать из или записать в страницу-«ловушку» сразу под стеком. «Ловушка» здесь — это невыделенная страница, к слову зарезервированная и потому никому не выделяемая даже случайно. Когда программа проверяет, не переполнен ли стек? Например, для Windows: программа может обработать SEH. Что происходит при переполнении стека? Например, для Windows: операционная система генерирует исключение EXCEPTION_STACK_OVERFLOW (определено в winnt.h). Если программа не обработает полученное исключение, а попытается использовать память за выделенным диапазоном - возникнет исключение "нарушение доступа". А вот в этом случае операционная система прибъет не только поток, который попытался воспользоваться большем чем ему доступно памятью стека потока, но и завершит аварийно весь процесс. ADD: Про *nix системы надо почитать, пока освежил знания по Рихтеру)

Ответ 2



Железный стек процессора (в x86) - это два регистра: ESP и SS. SS - это адрес сегмента стека (адрес самого стека в памяти) ESP - это указатель на текущее положение последнего элемента в стеке. В самом начале он выставляется на конец стека. И при помещении новых элементов - он уменьшается. т.е. стек в памяти выглядит так другие данные][ пустое место в стеке | занятое место в стеке ][другие данные ^--- расстояние = значению ESP ---^ по мере заполнения картина меняется на другие данные][ | занятое место в стеке ][другие данные ^^ и в конце концов ESP достигает значения 0 и уходит в минус. процессор оказывается в ситуации стек заполнен разбить на две части процессор его не может - т.к. это невозможно организовать на двух регистрах. и процессор слишком туп для этого. не его это дело. расширить стек он не может - он подперт другими данными с двух сторон. Ну и опять же - не его это дело. переместить стек в другое место он не может - Не его это дело. Вдруг разработчик передал адрес чего-то в стеке в стронний код и не ожидает, что стек переедет? Может только упасть. Процессор кидает ошибку Stack-Segment Fault, его ловит операционка. Операционка оказывается в точно такой же ситуации, что и процессор - не ее дело чинить переполненный стек + она не может принять решение за программиста. Так что она разбирается с программой в меру своих возмоностей - прокидывает ошибку в виде исключения SEH, Exception в .NET или просто убивает процесс. Теоретически корректно побороть Stack Overflow может рантайм с автоматическим управлением памяти (вроде .NET) - тем, что он может спрятать разработчика детали реализации, и запретить захватывать указатели на объекты в стеке. Это уберет последнее ограничение в списке, и позволит переместить стек в другую область памяти, выделив под него побольше места. На практике ни один фрейморк так не делает из соображений простоты и производительности и красоты. Stack Overflow - это почти всегда ошибка разработчика. Лучше (и проще!) упасть, и заставить его исправить ошибку, чем скрывать ее путем скрытого перемещения больших объемов памяти. Стоит отметить, что описание выше значительно упрощено - примерно до уровня x86 protected mode времен 486/Pentium. На самом деле современные операционные системы выделяют физическую память страницами + используют флаги защиты на страницах для обнаружения переполнения стека. Подробно (с картинками) об этом можно почитать у Рихтера, в книге "Создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows", главе "Стек потока". Рост в сторону уменьшения сделан ради упрощения проверки - на 0 проверять проще, да и размер стека хранить не нужно. На других архитектурах (не x86) стек вполне может расти в другую сторону - но проблему это не решает - т.к. все ограничения из списка выше остаются актуальными.

Ответ 3



Как стек знает что он переполнен? никак. Он не умеет думать. Когда програма проверяет не переполнен ли стек? а программа этого не делает. Это задача операционной системы. Она выделяет приложению немного памяти под стек (обычно 1 мегабайт, и это можно настраивать). Как только приложение начинает писать в много данных в стек и выходит за пределы выделенного места - операционная система прибивает приложение. Но в целом, приложение может знать, сколько места под стек ему выделено и сколько стека по факту уже занято. И если занятно больше какого-то объёма (допустим 95%), записать в лог/написать письмо. Что происходит при переполнении стека? Обычно операционная система прибивает приложение и дальше может сделать дамп памяти или запустить отладчик. Но это уже проблема не приложения

Ответ 4



Как стек знает, что он переполнен? По мере заполнения указатель стека уменьшается. А он, к слову сказать, беззнаковый. Когда он достигнет нуля, очередная попытка что-то в него поместить вызовет ошибку переполнения (ну на самом дел заёма). Вот тебе и сигнал. Когда программа проверяет, не переполнен ли стек? Программа тупо проверяет текущее значение указателя стека. Если она очень тупая, то на нулевое значение, если не очень тупая - то сравнивает его с неким пороговым, ниже которого могут возникнуть проблемы. Но это как. А когда? да когда программист решил, что надо проверять... Что происходит при переполнении стека? В лучшем случае - плевок в неиспользуемую область памяти. В плохом - ошибка защиты памяти. В худшем - неопределяемое разрушение памяти, чреватое подменой.

Комментариев нет:

Отправить комментарий