Распределение памяти на основе стека - Stack-based memory allocation

Типичный стек, хранящий локальные данные и информацию о вызовах вложенных процедур (не обязательно вложенные процедуры ). Эта стопка растет вниз от своего источника. Указатель стека указывает на текущий самый верхний датум в стеке. Операция push уменьшает указатель и копирует данные в стек; операция pop копирует данные из стека, а затем увеличивает указатель на единицу. Каждая процедура, вызываемая в программе, сохраняет информацию о возвращении процедуры (желтым цветом) и локальные данные (другими цветами), помещая их в стек.

Стеки в вычислительных архитектурах области объем памяти где данные добавляются или удаляются в последний пришел - первый ушел (LIFO) манера.

В большинстве современных компьютерных систем каждая нить имеет зарезервированную область памяти, называемую стеком. Когда функция выполняется, она может добавить некоторые данные своего локального состояния в верхнюю часть стека; когда функция завершает работу, она отвечает за удаление этих данных из стека. Как минимум, стек потока используется для хранения местоположения адреса возврата, предоставленного вызывающей стороной, чтобы позволить операторам возврата вернуться в правильное местоположение. Стек часто используется для хранения переменных фиксированной длины, локальных для текущих активных функций. Программисты могут также выбрать явное использование стека для хранения локальных данных переменной длины. Если область памяти находится в стеке потока, говорят, что эта память была выделена в стеке, т.е. выделение памяти на основе стека.

Преимущества и недостатки

Поскольку данные добавляются и удаляются в порядке очереди, выделение памяти на основе стека очень просто и обычно намного быстрее, чем выделение памяти на основе кучи (также известное как распределение динамической памяти ) обычно выделяется через malloc. Другая особенность заключается в том, что память в стеке автоматически и очень эффективно освобождается при выходе из функции, что может быть удобно для программиста, если данные больше не требуются.[1] (То же самое относится к longjmp если он переместился в точку до вызова alloca Однако, если данные необходимо сохранить в какой-либо форме, их необходимо скопировать из стека в кучу до выхода из функции. Следовательно, выделение на основе стека подходит для временных данных или данных, которые больше не требуются после выхода из текущей функции.

Размер стека, назначенный потоку, может составлять всего несколько байтов на некоторых небольших процессорах. Выделение в стеке большего объема памяти, чем доступно, может привести к крушение из-за переполнение стека. Вот почему функции, использующие alloca обычно не могут быть встроены:[2] если такая функция будет встроена в цикл, вызывающая сторона пострадает от непредвиденного роста использования стека, что значительно повысит вероятность переполнения.

Распределение на основе стека также может вызвать незначительные проблемы с производительностью: оно приводит к кадрам стека переменного размера, так что оба указатели стека и кадра необходимо управлять (с кадрами стека фиксированного размера один из них является избыточным). Обычно это намного дешевле, чем звонок маллок и свободный так или иначе. В частности, если текущая функция содержит оба вызова alloca и блоки, содержащие локальные данные переменной длины, то возникает конфликт между попытками alloca увеличить текущий кадр стека до тех пор, пока текущая функция не завершится, по сравнению с необходимостью компилятора разместить локальные переменные переменной длины в то же место в кадре стека. Этот конфликт обычно разрешается путем создания отдельной цепочки кучи для каждого вызова alloca (см.: https://code.woboq.org/gcc/libiberty/alloca.c.html ). Цепочка записывает глубину стека, на которой происходит каждое выделение, последующие вызовы alloca в любой функции обрезают эту цепочку до текущей глубины стека, чтобы в конечном итоге (но не сразу) освободить любое хранилище в этой цепочке. Вызов alloca с нулевым аргументом также может использоваться для освобождения памяти без выделения дополнительной памяти. Вследствие этого конфликта между alloca и хранилищем локальных переменных использование alloca может быть не более эффективным, чем использование malloc.

Системный интерфейс

Много Unix-подобный системы, а также Майкрософт Виндоус реализовать функцию под названием alloca для динамического выделения памяти стека способом, аналогичным основанному на куче маллок. Компилятор обычно переводит его во встроенные инструкции, управляющие указателем стека, аналогично тому, как массивы переменной длины обрабатываются.[3] Хотя нет необходимости явно освобождать память, существует риск неопределенного поведения из-за переполнения стека.[4] Функция присутствовала в системах Unix еще в 32 / В (1978), но не является частью Стандарт C или любой POSIX стандарт.

Более безопасная версия alloca называется _malloca, который сообщает об ошибках, существует в Microsoft Windows. Это требует использования _freea.[5] гнулиб предоставляет эквивалентный интерфейс, хотя вместо того, чтобы генерировать исключение SEH при переполнении, он делегирует маллок при обнаружении слишком большого размера.[6] Аналогичную функцию можно эмулировать с помощью ручного учета и проверки размера, например, при использовании alloca_account в glibc.[7]

Некоторые семейства процессоров, например x86, имеют специальные инструкции для управления стеком выполняемого в данный момент потока. Другие семейства процессоров, включая PowerPC и MIPS, не имеют явной поддержки стека, но вместо этого полагаются на соглашение и делегируют управление стеком операционной системе двоичный интерфейс приложения (ABI).

Рекомендации

  1. ^ «Преимущества Alloca». Библиотека GNU C.
  2. ^ "В соответствии". Использование коллекции компиляторов GNU (GCC).
  3. ^ alloca (3) – Linux Программиста Руководство - Библиотечные функции
  4. ^ «Почему использование alloca () не считается хорошей практикой?». stackoverflow.com. Получено 2016-01-05.
  5. ^ "_malloca". Документация Microsoft CRT.
  6. ^ "gnulib / malloca.h". GitHub. Получено 24 ноября 2019.
  7. ^ "glibc / include / alloca.h". Зеркала Берена Минора. 23 ноября 2019.

Смотрите также