Что происходит, когда микроконтроллерам не хватает оперативной памяти?

12

Это может быть просто совпадением, но я заметил, что микроконтроллеры, которые я использовал, перезагружались, когда у них заканчивалось ОЗУ (Atmega 328, если это зависит от оборудования). Это то, что делают микроконтроллеры, когда им не хватает памяти? Если нет, что происходит тогда?

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

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

Обновить

Я должен отметить, что я особенно заинтересован в фактическом механизме повреждения памяти (это результат переворачивания SP -> это зависит от отображения памяти uC и т. Д.)?

Мистер Мистер
источник
8
Некоторые микро-устройства будут сброшены, если вы попытаетесь получить доступ к неверным адресам Это ценная функция, реализованная в оборудовании. В других случаях это может привести к прыжку куда-нибудь произвольно (скажем, вы перекрыли адрес возврата для ISR), возможно, к выполнению данных, а не к коду, если архитектура это позволяет, и, возможно, к тому, что сторожевой таймер выведет их из цикла. из.
Спехро Пефхани
2
Процессор не может исчерпать ОЗУ, нет инструкции, которая заставит его исчерпать ОЗУ. Недостаток ОЗУ - это полностью программная концепция.
user253751

Ответы:

14

В общем случае стек и куча врезаются друг в друга. В этот момент все становится грязным.

В зависимости от MCU может произойти (или произойдет) одно из нескольких событий.

  1. Переменные повреждены
  2. Стек поврежден
  3. Программа повреждена

Когда случается 1, вы начинаете странно себя вести - дела идут не так, как должны. Когда случается 2, все виды ада ослабевают. Если адрес возврата в стеке (если он есть) поврежден, то, куда вернется текущий вызов, остается только догадываться. В то время в основном MCU начнет делать случайные вещи. Когда снова случится 3, кто точно знает, что произойдет. Это происходит только тогда, когда вы выполняете код из ОЗУ.

В общем, когда стек поврежден, все кончено. То, что происходит, зависит от MCU.

Может случиться так, что попытка выделить память в первую очередь не удастся, поэтому повреждение не произойдет. В этом случае MCU может вызвать исключение. Если не установлен обработчик исключений, то чаще всего MCU просто останавливается (эквивалент while (1);. Если установлен обработчик, он может перезагрузиться чисто.

Если выделение памяти происходит, или если оно пытается, терпит неудачу и просто продолжается без выделения памяти, то вы попадаете в сферу «кто знает?». MCU может в конечном итоге перезагрузить себя через правильную комбинацию событий (прерывания вызвали сброс микросхемы и т. Д.), Но нет гарантии, что это произойдет.

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

Majenko
источник
Спасибо за ваш ответ, это отличная сводка эффектов. Возможно, мне следовало указать, однако, что я хотел бы получить более подробную информацию о действительном механизме этих повреждений: выделена ли вся ОЗУ под стек и кучу, чтобы указатель стека переворачивал и перезаписывал более ранние переменные / адреса? Или это меньше зависит от отображения памяти каждого микро? По желанию (возможно, это тема сама по себе), мне было бы интересно узнать, как реализованы эти аппаратные обработчики.
Мистер Мистер
1
В основном это зависит от используемого компилятора и стандартной библиотеки C. Это также иногда зависит от того, как настроен компилятор (скрипты компоновщика и т. Д.).
Majenko
Не могли бы вы рассказать об этом, возможно, с помощью пары примеров?
Мистер Мистер
Не на самом деле нет. Некоторые системы выделяют конечное пространство для разных сегментов, некоторые нет. Некоторые используют сценарии компоновщика для определения сегментов, некоторые нет. Выберите интересующий вас микроконтроллер и
выясните,
12

Альтернативный взгляд: микроконтроллерам не хватает памяти.

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

Однако, как говорят Majenko и другие, плохо запрограммированный микроконтроллер может исчерпать память, а затем сделать что-нибудь, включая бесконечный цикл (который, по крайней мере, дает сторожевому таймеру шанс сбросить его. Вы включили сторожевой таймер, не так ли? )

Общие правила программирования для микроконтроллеров избегают этого: например, вся память либо выделяется в стеке, либо статически (глобально) выделяется; «new» или «malloc» запрещены. Так же как и рекурсия, так что максимальная глубина вложенности подпрограммы может быть проанализирована и показана для размещения в доступном стеке.

Таким образом, максимальная требуемая память может быть вычислена при компиляции или компоновке программы и сравнена с объемом памяти (часто кодируемым в сценарии компоновщика) для конкретного процессора, на который вы нацелены.

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

  • переписать, поменьше или
  • выберите больший процессор (они часто доступны с разным объемом памяти).

Одним из общих правил программирования микроконтроллеров является MISRA-C , принятый в автомобильной промышленности.

На мой взгляд, лучшая практика - использовать подмножество Ады SPARK-2014 . На самом деле Ada достаточно хорошо ориентируется на небольшие контроллеры, такие как AVR, MSP430 и ARM Cortex, и, по сути, обеспечивает лучшую модель для программирования микроконтроллеров, чем C. Но SPARK добавляет в программу аннотации в виде комментариев, которые описывают, что делает программа.

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

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

Сравнение MISRA-C и SPARK

Брайан Драммонд
источник
3
+1 это. Портирование malloc()(и это компаньон C ++ new) на AVR - это одна из худших вещей, которую могли сделать люди с Arduino, и это привело к тому, что многие, очень запутанные программисты со сломанным кодом на форуме и обменом стеками Arduino. Есть очень и очень мало ситуаций, когда mallocполезно иметь ATmega.
Коннор Вольф
3
+1 за философию, -1 за реализм. Если бы все было запрограммировано правильно, в этом вопросе не было бы необходимости. Вопрос заключался в том, что происходит, когда микроконтроллерам не хватает памяти. Как предотвратить их исчерпание памяти - другой вопрос. С другой стороны, рекурсия - это мощный инструмент, как для решения проблем, так и для выхода из стека.
PkP
2
@ Брайан, так как я не идиот, я, очевидно, согласен с тобой. Мне просто нравится думать об этом с обратной точки зрения - я хотел бы надеяться, что когда вы поймете ужасные последствия нехватки памяти (стека), вы будете искать способы предотвратить это. Таким образом, у вас есть реальный стимул к поиску хороших методов программирования, а не просто к выполнению хороших советов по программированию ... и когда вы преодолеете барьер памяти, вы с большей вероятностью будете применять эти методы даже за счет удобства. Это просто точка зрения ...
PkP
2
@PkP: слышу тебя громко и ясно. Я назвал это альтернативным представлением - потому что оно на самом деле не отвечает на вопрос!
Брайан Драммонд
2
@ MisterMystère: микроконтроллерам обычно не хватает памяти. Микроконтроллер, имеющий 4096 байтов оперативной памяти при первом включении, будет иметь 4096 байтов навсегда. Возможно, что код ошибочно попытается получить доступ к адресам, которые не существуют, или ожидать, что два разных метода вычисления адресов получат доступ к разной памяти, когда они этого не делают, но сам контроллер просто выполнит инструкции, которые он дал.
суперкат
6

Мне очень нравится ответ Majenko и +1 я сам. Но я хочу уточнить это до острого момента:

Когда микроконтроллеру не хватает памяти, может случиться что угодно .

Вы действительно не можете положиться ни на что, когда это происходит. Когда у машины заканчивается память стека, стек, скорее всего, поврежден. И когда это произойдет, все может случиться. Значения переменных, разливы, временные регистры становятся поврежденными, нарушая выполнение программ. Если / то / elses может оценить неправильно. Обратные адреса искажаются, заставляя программу переходить на случайные адреса. Любой код, который вы написали в программе, может выполняться. (Рассмотрим код вроде: «if [условие], то {fire_all_missiles ();}»). Кроме того, целый набор инструкций, которые вы не написали, может выполняться, когда ядро ​​переходит в несвязанную область памяти. Все ставки сделаны.

PKP
источник
2
Спасибо за добавление, мне особенно понравилась строка fire_all_missiles ().
Мистер Мистер
1

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

Ilia
источник