Стек
Стек — область оперативной памяти, куда процессор складывает адреса возврата из подпрограмм и прерываний, сохранённые регистры и локальные переменные функций. Он растёт и уменьшается автоматически, а его переполнение — одна из самых коварных ошибок встраиваемых программ.
Обновлено Редакция GAW
Стек (stack) — область оперативной памяти, организованная по принципу «последним пришёл — первым ушёл». Процессор кладёт в него адрес возврата при вызове подпрограммы и при входе в прерывание, функции сохраняют в нём регистры и размещают локальные переменные. Положение вершины стека хранит регистр — указатель стека (stack pointer, SP). У AVR и Cortex-M стек растёт от старших адресов к младшим.
Стек у AVR и Cortex-M3
| ATmega328P | Cortex-M3 (STM32F1) | |
|---|---|---|
| Указатель стека | SPH:SPL | R13 в двух копиях: MSP и PSP |
| Начальное значение | конец SRAM, 0x08FF — уже после сброса | MSP загружается из адреса 0x0000 0000 |
| Что сохраняется при прерывании | только адрес возврата | восемь регистров, аппаратно |
У ATmega328P вызов подпрограммы кладёт в стек 2 байта адреса возврата, у ATmega2560 с флеш-памятью больше 128 КБ — 3 байта (DS40002061B, глава 7). Подробнее — в статье об архитектуре AVR.
Cortex-M3 при входе в исключение сам сохраняет в стек восемь регистров — это часть задержки прерывания. Обработчики исключений всегда работают на основном стеке MSP, а стек процесса PSP операционные системы выделяют каждой задаче. Подробнее — в статье об архитектуре Cortex-M3.
Переполнение стека
Стек и переменные делят одну оперативную память: глубокая рекурсия, большой локальный массив или много вложенных прерываний могут незаметно затереть глобальные переменные. Признаки — странное поведение, далёкое от реальной причины. Защита — оценивать глубину стека, не размещать большие массивы в локальных переменных, а на Cortex-M с блоком MPU ставить ниже стека защитную область без доступа: тогда переполнение сразу вызывает отказ, а не тихо портит данные.
Частые вопросы
Чем MSP отличается от PSP?
Это две копии указателя стека R13 в ядре Cortex-M3. Основной стек MSP загружается после сброса из адреса 0x0000 0000, и на нём всегда работают обработчики исключений. Стек процесса PSP включают для режима Thread битом SPSEL регистра CONTROL. Программы без ОС обычно обходятся одним MSP, а ОС реального времени дают каждой задаче свой стек на PSP.
Что будет при переполнении стека?
Стек растёт навстречу переменным в той же оперативной памяти и начинает затирать их. Программа ведёт себя странно и непредсказуемо, а ошибка проявляется далеко от места, где она возникла. Типичные причины — глубокая рекурсия, большие локальные массивы и вложенные прерывания. На Cortex-M с блоком MPU ниже стека ставят защитную область без доступа, и переполнение сразу вызывает отказ.
Сколько стека занимает вызов функции на AVR?
Минимум — адрес возврата: 2 байта при 16-битном счётчике команд и 3 байта у кристаллов с флеш-памятью больше 128 КБ, например ATmega2560. К нему добавляются сохранённые регистры и локальные переменные функции. Прерывание тоже кладёт в стек адрес возврата, а компилятор в функции ISR() — ещё SREG и используемые регистры.
Источники
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B — гл. 7 — указатель стека SPH:SPL, после сброса равен концу SRAM; стек растёт к младшим адресам
- STMicroelectronics. PM0056: STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual, Rev 7, 2024 — регистры MSP и PSP, бит SPSEL регистра CONTROL, загрузка MSP из адреса 0x0000 0000 после сброса
Нашли ошибку?