Это может быть просто совпадением, но я заметил, что микроконтроллеры, которые я использовал, перезагружались, когда у них заканчивалось ОЗУ (Atmega 328, если это зависит от оборудования). Это то, что делают микроконтроллеры, когда им не хватает памяти? Если нет, что происходит тогда?
Почему как? Указатель стека, безусловно, слепо увеличивается до нераспределенного диапазона памяти (или переворачивается), но что происходит потом: существует ли какая-то защита, которая заставляет его перезагружаться, или это (среди других эффектов) результат перезаписи критического данные (которые, как я полагаю, отличаются от кода, который, я думаю, запускается непосредственно из флэш-памяти)?
Я не уверен, что это должно быть здесь или в переполнении стека, пожалуйста, дайте мне знать, если это нужно перенести, хотя я почти уверен, что оборудование играет в этом роль.
Обновить
Я должен отметить, что я особенно заинтересован в фактическом механизме повреждения памяти (это результат переворачивания SP -> это зависит от отображения памяти uC и т. Д.)?
источник
Ответы:
В общем случае стек и куча врезаются друг в друга. В этот момент все становится грязным.
В зависимости от MCU может произойти (или произойдет) одно из нескольких событий.
Когда случается 1, вы начинаете странно себя вести - дела идут не так, как должны. Когда случается 2, все виды ада ослабевают. Если адрес возврата в стеке (если он есть) поврежден, то, куда вернется текущий вызов, остается только догадываться. В то время в основном MCU начнет делать случайные вещи. Когда снова случится 3, кто точно знает, что произойдет. Это происходит только тогда, когда вы выполняете код из ОЗУ.
В общем, когда стек поврежден, все кончено. То, что происходит, зависит от MCU.
Может случиться так, что попытка выделить память в первую очередь не удастся, поэтому повреждение не произойдет. В этом случае MCU может вызвать исключение. Если не установлен обработчик исключений, то чаще всего MCU просто останавливается (эквивалент
while (1);
. Если установлен обработчик, он может перезагрузиться чисто.Если выделение памяти происходит, или если оно пытается, терпит неудачу и просто продолжается без выделения памяти, то вы попадаете в сферу «кто знает?». MCU может в конечном итоге перезагрузить себя через правильную комбинацию событий (прерывания вызвали сброс микросхемы и т. Д.), Но нет гарантии, что это произойдет.
Обычно существует высокая вероятность того, что это произойдет, хотя, если он включен, это внутренний сторожевой таймер (если таковой существует), тайм-аут и перезагрузка чипа. Когда программа полностью выходит из строя через такой вид сбоя, инструкции по сбросу таймера, как правило, не будут выполняться, поэтому произойдет тайм-аут и сброс.
источник
Альтернативный взгляд: микроконтроллерам не хватает памяти.
По крайней мере, не при правильном программировании. Программирование микроконтроллера не совсем то же самое, что программирование общего назначения, чтобы сделать это правильно, вы должны знать о его ограничениях и программировать соответственно. Есть инструменты, которые помогут обеспечить это. Ищите их и изучайте их - по крайней мере, как читать сценарии компоновщика и предупреждения.
Однако, как говорят Majenko и другие, плохо запрограммированный микроконтроллер может исчерпать память, а затем сделать что-нибудь, включая бесконечный цикл (который, по крайней мере, дает сторожевому таймеру шанс сбросить его. Вы включили сторожевой таймер, не так ли? )
Общие правила программирования для микроконтроллеров избегают этого: например, вся память либо выделяется в стеке, либо статически (глобально) выделяется; «new» или «malloc» запрещены. Так же как и рекурсия, так что максимальная глубина вложенности подпрограммы может быть проанализирована и показана для размещения в доступном стеке.
Таким образом, максимальная требуемая память может быть вычислена при компиляции или компоновке программы и сравнена с объемом памяти (часто кодируемым в сценарии компоновщика) для конкретного процессора, на который вы нацелены.
Тогда микроконтроллеру может не хватить памяти, но ваша программа может. И в этом случае вы получите
Одним из общих правил программирования микроконтроллеров является MISRA-C , принятый в автомобильной промышленности.
На мой взгляд, лучшая практика - использовать подмножество Ады SPARK-2014 . На самом деле Ada достаточно хорошо ориентируется на небольшие контроллеры, такие как AVR, MSP430 и ARM Cortex, и, по сути, обеспечивает лучшую модель для программирования микроконтроллеров, чем C. Но SPARK добавляет в программу аннотации в виде комментариев, которые описывают, что делает программа.
Теперь инструменты SPARK будут анализировать программу, включая эти аннотации, и проверять ее свойства (или сообщать о потенциальных ошибках). Вам не нужно тратить время или пространство кода на ошибочные обращения к памяти или целочисленные переполнения, поскольку доказано, что они никогда не происходят.
Несмотря на то, что SPARK требует дополнительной предварительной работы, опыт показывает, что он может получить продукт быстрее и дешевле, потому что вы не тратите время на погоню за таинственными перезагрузками и другим странным поведением.
Сравнение MISRA-C и SPARK
источник
malloc()
(и это компаньон C ++new
) на AVR - это одна из худших вещей, которую могли сделать люди с Arduino, и это привело к тому, что многие, очень запутанные программисты со сломанным кодом на форуме и обменом стеками Arduino. Есть очень и очень мало ситуаций, когдаmalloc
полезно иметь ATmega.Мне очень нравится ответ Majenko и +1 я сам. Но я хочу уточнить это до острого момента:
Когда микроконтроллеру не хватает памяти, может случиться что угодно .
Вы действительно не можете положиться ни на что, когда это происходит. Когда у машины заканчивается память стека, стек, скорее всего, поврежден. И когда это произойдет, все может случиться. Значения переменных, разливы, временные регистры становятся поврежденными, нарушая выполнение программ. Если / то / elses может оценить неправильно. Обратные адреса искажаются, заставляя программу переходить на случайные адреса. Любой код, который вы написали в программе, может выполняться. (Рассмотрим код вроде: «if [условие], то {fire_all_missiles ();}»). Кроме того, целый набор инструкций, которые вы не написали, может выполняться, когда ядро переходит в несвязанную область памяти. Все ставки сделаны.
источник
AVR сбросил вектор с нулевого адреса. Когда вы перезаписываете стек со случайным мусором, вы в конечном итоге зацикливаетесь и перезаписываете какой-то адрес возврата, и он будет указывать на «никуда»; затем, когда вы возвращаетесь из подпрограммы в эту никуда, выполнение зацикливается на адресе 0, где обычно происходит переход к обработчику сброса.
источник