Вкратце объясните концепцию фрейма стека

Кажется, я понял идею стека вызовов в дизайне языков программирования. Но я не могу найти (вероятно, я просто не ищу достаточно) достойного объяснения того, что такое stack frame.

Поэтому я хотел бы попросить кого-нибудь объяснить мне это в нескольких словах.


person ikostia    schedule 07.04.2012    source источник


Ответы (6)


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

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

РЕДАКТИРОВАТЬ:

Существует большая разница между стеками вызовов более высокого уровня и стеком вызовов процессора.

Когда мы говорим о стеке вызовов процессора, мы говорим о работе с адресами и значениями на уровне байтов / слов в ассемблере или машинном коде. Когда говорят о языках более высокого уровня, существуют «стеки вызовов», но они представляют собой инструмент отладки / времени выполнения, управляемый средой выполнения, так что вы можете регистрировать, что пошло не так с вашей программой (на высоком уровне). На этом уровне часто известны такие вещи, как номера строк, имена методов и классов. К тому времени, когда процессор получает код, он совершенно не понимает этих вещей.

person Tony R    schedule 07.04.2012
comment
Процессор знает, сколько байтов находится в каждом кадре, и соответственно перемещает указатель стека по мере того, как кадры выталкиваются и выталкиваются из стека. - Я сомневаюсь, что процессор что-то знает о стеке, потому что МЫ манипулируем им с помощью суббазирования (выделения), нажатия и выталкивания. Итак, вот соглашения о вызовах, которые объясняют, как мы должны использовать стек. - person Victor Polevoy; 18.04.2018

Если вы хорошо разбираетесь в стеке, тогда вы поймете, как работает память в программе, и если вы поймете, как работает память в программе, вы поймете, как хранятся функции в программе, и если вы поймете, как хранятся функции в программе, вы поймете, как работает рекурсивная функция, и если вы понимаете, как работает рекурсивная функция, вы поймете, как работает компилятор, и если вы поймете, как работает компилятор, ваш ум будет работать как компилятор, и вы очень легко отладите любую программу

Позвольте мне объяснить, как работает стек:

Сначала вы должны знать, как функции представлены в стеке:

Куча хранит динамически назначаемые значения.
Стек хранит значения автоматического выделения и удаления.

введите описание изображения здесь

Давайте разберемся на примере:

def hello(x):
    if x==1:
        return "op"
    else:
        u=1
        e=12
        s=hello(x-1)
        e+=1
        print(s)
        print(x)
        u+=1
    return e

hello(4)

Теперь разберитесь в частях этой программы:

введите описание изображения здесь

Теперь посмотрим, что такое стек и что такое части стека:

введите описание изображения здесь

Размещение стека:

Помните одну вещь: если условие возврата какой-либо функции будет выполнено, независимо от того, загрузили оно локальные переменные или нет, оно немедленно вернется из стека с его фреймом стека. Это означает, что всякий раз, когда какая-либо рекурсивная функция удовлетворяет базовое условие, и мы помещаем возврат после базового условия, базовое условие не будет ждать загрузки локальных переменных, которые находятся в части «else» программы. Он немедленно вернет текущий кадр из стека, после чего следующий кадр теперь находится в записи активации.

Посмотрите это на практике:

введите описание изображения здесь

Выделение блока:

Итак, теперь всякий раз, когда функция встречает оператор return, она удаляет текущий кадр из стека.

При возврате из стека значения будут возвращены в обратном порядке, в котором они были размещены в стеке.

введите описание изображения здесь

person Aaditya Ura    schedule 18.10.2016
comment
стек растет вниз, а куча растет вверх, на диаграмме они перевернуты. ПРАВИЛЬНАЯ ДИАГРАММА ЗДЕСЬ - person Rafael; 01.11.2016
comment
@Rafael извините за путаницу, я говорил о направлении роста, я не говорил о направлении роста стека. Есть разница между направлением роста и направлением роста стека. См. Здесь stackoverflow.com/questions/1677415/ - person Aaditya Ura; 07.11.2016
comment
Рафаэль прав. Также первое изображение неверно. Замените его чем-нибудь другим (поищите в Google картинки для стека кучи). - person KeyC0de; 20.06.2017
comment
Итак, если я правильно понимаю, на вашей третьей диаграмме есть 3 кадра стека, потому что hello() рекурсивно вызвал hello(), который затем (снова) рекурсивно вызвал hello(), а глобальный кадр - это исходная функция, которая вызвала первый hello()? - person Andy J; 14.07.2018
comment
Куда нас ведут ссылки ?? Эти ссылки должны быть удалены как можно скорее, поскольку представляют серьезную проблему для безопасности. - person Shivanshu; 28.04.2019

Быстрое завершение. Может у кого-то есть объяснение получше.

Стек вызовов состоит из одного или нескольких кадров стека. Каждый кадр стека соответствует вызову функции или процедуры, который еще не завершился возвратом.

Чтобы использовать кадр стека, поток хранит два указателя, один называется указателем стека (SP), а другой - указателем кадра (FP). SP всегда указывает на «верх» стека, а FP всегда указывает на «верх» кадра. Кроме того, поток также поддерживает счетчик программ (ПК), который указывает на следующую инструкцию, которая должна быть выполнена.

В стеке хранятся: локальные переменные и временные, фактические параметры текущей инструкции (процедура, функция и т. Д.)

Существуют разные соглашения о вызовах, касающиеся очистки стека.

person ervinbosenbacher    schedule 07.04.2012
comment
Не забывайте, что адрес возврата подпрограммы помещается в стек. - person Tony R; 07.04.2012
comment
Указатель кадра также является базовым указателем в терминах x86. - person peterchaula; 09.04.2017
comment
Подчеркну, что указатель кадра указывает на начало кадра стека для текущего активного воплощения процедуры. - person Server Khalilov; 04.10.2017

«Стек вызовов состоит из фреймов стека ...» - Википедия

Фрейм стека - это вещь, которую вы кладете в стек. Это структуры данных, содержащие информацию о вызываемых подпрограммах.

person Waleed Khan    schedule 07.04.2012
comment
Извините, я понятия не имею, как я пропустил это в вики. Спасибо. Правильно ли я понимаю, что в динамических языках размер кадра не является постоянным значением, поскольку локальные переменные функции точно не известны? - person ikostia; 07.04.2012
comment
Размер и характер рамы сильно зависят от архитектуры машины. Фактически, сама парадигма стека вызовов зависит от архитектуры. Насколько я знаю, он всегда переменный, потому что разные вызовы функций будут иметь разное количество данных аргумента. - person Tony R; 07.04.2012
comment
Обратите внимание, что размер кадра стека должен быть известен процессору, когда он обрабатывается. Когда это происходит, размер данных уже определен. Динамические языки компилируются в машинный код так же, как статические языки, но часто выполняются точно в срок, так что компилятор может поддерживать динамизм, а процессор может работать с известными размерами фреймов. Не путайте языки более высокого уровня с машинным кодом / сборкой, где все это на самом деле и происходит. - person Tony R; 07.04.2012
comment
Но ведь у динамических языков тоже есть свои стеки вызовов, не так ли? Я имею в виду, если, скажем, Python хочет выполнить некоторую процедуру, данные об этой процедуре хранятся внутри некоторой структуры интерпретатора Python, я прав? Я имею в виду, что стек вызовов присутствует не только на низком уровне. - person ikostia; 07.04.2012
comment
Прочитав небольшую часть этой статьи в Википедии, я исправился (немного). Размер кадра стека может оставаться неизвестным во время компиляции. Но к тому времени, когда процессор будет работать с указателями стека + кадра, он должен знать, каковы их размеры. Размер может изменяться, но процессор знает размер, это то, что я пытался сказать. - person Tony R; 07.04.2012
comment
Кроме того, стеки могут присутствовать на более высоком уровне, но это будет своего рода виртуальный стек. У процессора есть собственный стек вызовов. Итак, опять же, не путайте их. - person Tony R; 07.04.2012
comment
Мне пришло в голову, что вы, наверное, путаете эти две вещи =). Когда вы программируете на более высоком уровне, вы часто перехватываете исключение, а затем распечатываете стек вызовов. Это совсем другое дело, чем печать стека вызовов процессора (что вы можете сделать, например, в Visual Studio, но он покажет вам ассемблерный код). Среда выполнения хранит стек вызовов функций, чтобы вы, как программист, могли отслеживать, что делает ваша программа. Но ответы, которые вы здесь получаете, касаются низкоуровневого стека вызовов процессора. - person Tony R; 08.04.2012
comment
Спасибо, но меня особенно заинтересовал второй. Возможно, я действительно перепутал их, но с моей точки зрения они очень похожи и служат для одинаковых целей. В любом случае, спасибо вам большое! - person ikostia; 08.04.2012
comment
Я лично не видел фреймы стека на высоком уровне, но похоже, что у Python есть такая концепция. Вот вопрос, который говорит об этом: stackoverflow.com/questions/3711184/ Похоже, ваш вопрос был специфичным для Python, и вы этого не осознавали = P - person Tony R; 08.04.2012
comment
Да, это вполне возможно. Не понимая, что такое стековый фрейм, я не мог понять, что он имеет отношение только к Python :) - person ikostia; 08.04.2012

У программистов могут возникнуть вопросы о кадрах стека не в широком смысле (что это отдельная сущность в стеке, которая обслуживает только один вызов функции и сохраняет адрес возврата, аргументы и локальные переменные), а в узком смысле - когда упоминается термин stack frames в контексте параметров компилятора.

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

Например, компилятор Microsoft Visual Studio 2015 C / C ++ имеет следующий параметр, связанный с stack frames:

  • / Oy (Пропуск указателя кадра)

GCC имеют следующее:

  • -fomit-frame-pointer (Не храните указатель кадра в регистре для функций, которым он не нужен. Это позволяет избежать инструкций по сохранению, настройке и восстановлению указателей кадра; он также делает дополнительный регистр доступным во многих функциях. )

Компилятор Intel C ++ имеет следующее:

  • -fomit-frame-pointer (Определяет, используется ли EBP как универсальный регистр при оптимизации)

который имеет следующий псевдоним:

  • /Oy

Delphi имеет следующий параметр командной строки:

  • - $ W + (Создать кадры стека)

В этом конкретном смысле, с точки зрения компилятора, фрейм стека - это просто код входа и выхода для подпрограммы, который помещает якорь в стек - который также можно использовать для отладки и для обработки исключений. . Инструменты отладки могут сканировать данные стека и использовать эти привязки для обратной трассировки, находя call sites в стеке, т.е. отображать имена функций в том порядке, в котором они были вызваны иерархически. Для архитектуры Intel это push ebp; mov ebp, esp или enter для входа и mov esp, ebp; pop ebp или leave для выхода.

Вот почему очень важно понимать, что такое стековый фрейм, когда дело доходит до параметров компилятора, потому что компилятор может контролировать, генерировать этот код или нет.

В некоторых случаях кадр стека (код входа и выхода для процедуры) может быть опущен компилятором, а доступ к переменным будет осуществляться напрямую через указатель стека (SP / ESP / RSP), а не через удобный базовый указатель (BP / ESP / RSP). Условия пропуска кадра стека, например:

  • функция является листовой функцией (то есть конечной сущностью, которая не вызывает другие функции);
  • отсутствуют конструкции try / finally, try / except или аналогичные, т.е. исключения не используются;
  • никакие процедуры не вызываются с исходящими параметрами в стеке;
  • функция не имеет параметров;
  • функция не имеет встроенного ассемблерного кода;
  • и т.д...

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

person Maxim Masiutin    schedule 14.07.2017

Фрейм стека - это упакованная информация, относящаяся к вызову функции. Эта информация обычно включает аргументы, переданные функции th, локальные переменные и место возврата после завершения. Запись активации - это другое название кадра стека. Компоновка фрейма стека определяется в ABI производителем, и каждый компилятор, поддерживающий ISA, должен соответствовать этому стандарту, однако схема компоновки может зависеть от компилятора. Обычно размер кадра стека не ограничен, но существует концепция, называемая «красная / защищенная зона», позволяющая системным вызовам ... и т. Д. Выполняться без вмешательства в кадр стека.

Всегда есть SP, но на некоторых ABI (например, ARM и PowerPC) FP не является обязательным. Аргументы, которые необходимо было поместить в стек, можно компенсировать только с помощью SP. Создается ли кадр стека для вызова функции или нет, зависит от типа и количества аргументов, локальных переменных и общего доступа к локальным переменным. На большинстве ISA сначала используются регистры, и если аргументов больше, чем регистров, выделенных для передачи аргументов, они помещаются в стек (например, x86 ABI имеет 6 регистров для передачи целочисленных аргументов). Следовательно, иногда некоторым функциям не требуется помещать фрейм стека в стек, просто адрес возврата помещается в стек.

person drunk teapot    schedule 28.02.2017