Ответ 1
Stack
Стек, в этом контексте, является последним в первом буфере, который вы размещаете во время выполнения вашей программы. Последнее, сначала (LIFO) означает, что последнее, что вы положили, - это всегда первая вещь, которую вы возвращаете - если вы нажмете 2 элемента в стеке, "A" и затем "B", то первое, что вы поп от стека будет "B", а следующая вещь - "A".
Когда вы вызываете функцию в своем коде, следующая команда после вызова функции хранится в стеке и любое пространство памяти, которое может быть перезаписано вызовом функции. Выбранная функция может использовать больше стека для собственных локальных переменных. Когда это будет сделано, он освободит место локального пространства переменных, которое он использовал, а затем вернется к предыдущей функции.
Переполнение стека
Переполнение стека - это когда вы использовали больше памяти для стека, чем предполагалось использовать в вашей программе. Во встроенных системах у вас может быть только 256 байт для стека, и если каждая функция занимает 32 байта, тогда вы можете иметь только вызовы функций 8 функции функции 2 с функцией глубокой функции 1, которая вызывает функцию 3, которая вызывает функцию 4.... кто звонит функция 8, которая вызывает функцию 9, но функция 9 перезаписывает память за пределами стека. Это может перезаписать память, код и т.д.
Многие программисты совершают эту ошибку, вызывая функцию A, которая затем вызывает функцию B, которая затем вызывает функцию C, которая затем вызывает функцию A. Она может работать большую часть времени, но только один раз неправильный ввод вызовет ее что круг навсегда, пока компьютер не узнает, что стек переполнен.
Рекурсивные функции также являются причиной этого, но если вы пишете рекурсивно (т.е. ваша функция вызывает себя), тогда вам нужно знать об этом и использовать статические/глобальные переменные для предотвращения бесконечной рекурсии.
Как правило, ОС и язык программирования, которым вы пользуетесь, управляют стеком, и это из ваших рук. Вы должны посмотреть на ваш график вызовов (древовидная структура, которая показывает с вашего основного момента, что каждая функция вызывает), чтобы увидеть, насколько глубоко ваши вызовы функций, и определить циклы и рекурсию, которые не предназначены. Преднамеренные циклы и рекурсия должны быть искусственно проверены на ошибку, если они называют друг друга слишком много раз.
Помимо хороших практик программирования, статического и динамического тестирования, вы не можете много сделать в этих системах высокого уровня.
Встроенные системы
Во встроенном мире, особенно в коде с высокой надежностью (автомобильная, авиационная, космическая), вы выполняете обширные проверки и проверку кода, но также выполняете следующие действия:
- Запретить рекурсию и циклы - соблюдение политики и тестирования
- Держите код и стек далеко друг от друга (код во флэш-памяти, стек в ОЗУ и никогда не будет соответствовать друг другу)
- Поместите защитные полосы вокруг стека - пустую область памяти, которую вы заполняете магическим числом (обычно это программа прерывания, но здесь есть много вариантов), а сотни или тысячи раз в секунду вы смотрите на защитные полосы чтобы убедиться, что они не были перезаписаны.
- Использовать защиту памяти (т.е. не выполнять в стеке, не читать или писать непосредственно за стек)
- Прерывания не вызывают вторичные функции - они устанавливают флаги, копируют данные и позволяют приложению заботиться о его обработке (в противном случае вы можете получить 8 в глубине вашего дерева вызовов функций, иметь прерывание, а затем выйти еще несколько функции внутри прерывания, вызывающие выброс). У вас несколько деревьев вызовов - одно для основных процессов и по одному для каждого прерывания. Если ваши прерывания могут прервать друг друга... ну, есть драконы...
Языки и системы высокого уровня
Но на языках высокого уровня, работающих в операционных системах:
- Уменьшите локальное хранилище переменных (локальные переменные хранятся в стеке), хотя компиляторы довольно умны в этом отношении и иногда помещают больших кусков в кучу, если ваше дерево вызовов является мелким)
- Избегайте или строго ограничивайте рекурсию
- Не прерывайте свои программы слишком далеко на меньшие и меньшие функции - даже без учета локальных переменных каждый вызов функции потребляет до 64 байтов в стеке (32-битный процессор, сохраняя половину регистров процессора, флаги и т.д.).
- Держите дерево вызовов неглубоким (аналогично приведенному выше описанию)
Веб-серверы
Это зависит от "песочницы", которую вы имеете, можете ли вы контролировать или даже видеть стек. Скорее всего, вы можете обрабатывать веб-серверы, как и любой другой язык высокого уровня и операционную систему, - это в значительной степени из ваших рук, но проверьте используемый язык и серверный стек. Например, можно разбить стек на вашем SQL-сервере.
-Adam