Если malloc может потерпеть неудачу, почему инициализация переменных стека не может (по крайней мере, мы не проверяем это)? - PullRequest
3 голосов
/ 01 марта 2012

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

Ответы [ 5 ]

4 голосов
/ 01 марта 2012

Да, вы получите ошибку времени переполнения стека.

Примечание: существует популярный веб-сайт, названный в честь этой самой ошибки!

4 голосов
/ 01 марта 2012

Распределение стека может дать сбой , и вы ничего не можете с этим поделать.

В современной ОС значительный объем памяти будет выделен для стека для начала (в Linux оно кажется 128k или около того) и a (обычно намного больше, например 8M) в Linux, и обычно настраиваемый) диапазон виртуальных адресов будет зарезервирован для роста стека. Если вы превысите зафиксированную часть, фиксация большего объема памяти может завершиться ошибкой из-за нехватки памяти, и ваша программа вылетит с SIGSEGV. Если вы превысите зарезервированный диапазон адресов, ваша программа определенно потерпит неудачу, возможно, катастрофически, если она закончит перезаписью других данных чуть ниже диапазона адресов стека.

Решение не состоит в том, чтобы делать безумные вещи со стеком. Даже начальная сумма в Linux (128 КБ) занимает больше места в стеке, чем вы когда-либо должны использовать. Не используйте рекурсию вызовов, если у вас нет логарифмической привязки к количеству уровней вызовов, не используйте гигантские автоматические массивы или структуры (включая те, которые могут быть результатом измерений VLA, предоставленных пользователем), и у вас все будет хорошо.

Обратите внимание, что не существует переносимого и безопасного для будущего способа измерения текущего использования стека и оставшейся доступности, поэтому вы просто должны быть в этом уверены.

Редактировать: Одна гарантия, которую у вас есть, о распределении стека, по крайней мере в реальных системах (без взлома сплит-стека), состоит в том, что пространство стека, которое вы уже убедились, у вас не будет волшебным образом исчезают. Например, если вы успешно один раз позвонили c() из b() из a() из main(), и они не используют никаких VLA, которые могут различаться по размеру, второе повторение этого же шаблона вызова в том же экземпляре ваша программа не подведет. Вы также можете найти инструменты для выполнения статического анализа в некоторых программах (программы без излишнего использования указателей функций и / или рекурсии), которые определят максимальный объем стекового пространства, когда-либо потребляемый вашей программой, после чего вы можете настроить проверку при запуске программы. что вы можете успешно использовать столько места, прежде чем продолжить.

1 голос
/ 01 марта 2012

Ну ... семантически говоря, стека нет.

С точки зрения языка, автоматическое хранилище просто работает , а динамическое хранилище может выйти из строя определенным образом (malloc возвращает NULL, new выдает std::bad_alloc).

Конечно, реализации обычно выводят стек для реализацииавтоматическое хранение, и тот, который при этом ограничен в размерах.Однако это деталь реализации , и не обязательно.

Например, gcc -fsplit-stack позволяет вам иметь дробный стек, который растет по мере необходимости.,Эта техника довольно недавняя для AFAIK на C или C ++, но языки с продолжениями (и тысячами или миллионами из них), такими как Haskell, имеют эту встроенную функцию, и Go тоже об этом говорит.

Тем не менее, в какой-то момент, память будет исчерпана, если вы продолжите стучать по ней.Это фактически неопределенное поведение, так как Стандарт вообще не пытается с этим справиться.В этом случае, как правило, ОС посылает сигнал программе, которая отключается, и стек не разматывается.

1 голос
/ 01 марта 2012

Процесс будет убит ОС, если ему не хватит места в стеке.

Точная механика зависит от ОС.Например, нехватка стекового пространства в Linux вызывает segfault .

0 голосов
/ 01 марта 2012

Хотя операционная система может не информировать вас о том, что у вас недостаточно места в стеке, вы можете проверить это самостоятельно с помощью встроенной сборки:

unsigned long StackSpace()
{
    unsigned long retn = 0;
    unsigned long *rv = &retn;

    __asm
    {
        mov eax, FS:[0x08]
        sub eax, esp
        mov [rv], eax
    }

    return retn;
}

Вы можете определить значение FS: [*], обратившись к окнам Блок информации о потоке

Редактировать: предназначен для вычитания esp из eax, а не ebx XD

...