Що відбувається, коли у мікроконтролерів закінчено оперативну пам'ять?


12

Це може бути просто збіг обставин, але я помітив мікроконтролери, які я перезавантажував, коли у них закінчувалася оперативна пам’ять (Atmega 328, якщо це специфічно для обладнання). Це те, що роблять мікроконтролери, коли у них не вистачає пам'яті? Якщо ні, що станеться тоді?

Чому / як? Покажчик стека, безумовно, сліпо збільшується до не виділеного діапазону пам’яті (або перевертається), але що станеться тоді: чи є якийсь захист, який змушує його перезавантажуватись чи це (серед інших ефектів) результат перезапису критичного дані (які я вважаю різними від коду, який, на мою думку, виконується безпосередньо з флеш)?

Я не впевнений, що це повинно бути тут чи на переповнюванні стека. Будь ласка, дайте мені знати, чи потрібно це перемістити, хоча я впевнений, що обладнання у цьому має роль.

Оновлення

Мені слід зазначити, що мене особливо цікавить власне механізм пошкодження пам’яті (це результат перенесення SP -> це залежить від відображення пам'яті UC тощо)?


8
Деякі мікрофони будуть скинуті, якщо ви спробуєте отримати доступ до недійсних адрес. Це цінна функція, реалізована в апараті. В іншому випадку це може перейти до куди завгодно довільного (скажімо, ви клобували зворотну адресу для ISR). з.
Spehro Pefhany

2
Процесор не може закінчитися оперативною пам’яттю, не існує інструкції, яка змусить його вичерпати оперативну пам’ять. Вичерпання оперативної пам’яті - це повністю програмне забезпечення.
користувач253751

Відповіді:


14

Взагалі стек і купа врізаються один в одного. У цей момент все стає безладним.

Залежно від MCU може статися (або буде) одна з кількох речей.

  1. Змінні пошкоджуються
  2. Стек псується
  3. Програма псується

Коли 1 трапляється, ти починаєш дивно поведінку - речі не роблять те, що слід. Коли 2 трапляється, всяке пекло проривається. Якщо зворотна адреса в стеці (якщо така є) пошкоджена, то там, де повернеться поточний дзвінок, - це хтось здогадався. В цей час MCU почне робити випадкові речі. Коли 3 повториться, хто знає, що було б. Це відбувається лише тоді, коли ви виконуєте код з оперативної пам'яті.

Взагалі, коли стек псується, він закінчується. Тільки те, що трапляється, полягає в MCU.

Можливо, спроба виділити пам'ять в першу чергу не вдається, тому корупція не трапиться. У цьому випадку MCU може створити виняток. Якщо не встановлений обробник винятків, найчастіше MCU просто зупиниться (еквівалент while (1);. Якщо встановлений обробник, він може перезавантажитися.

Якщо розподіл пам’яті продовжується, або якщо вона намагається, не вдається, і просто продовжується без виділення пам’яті, то ви перебуваєте в царинах «хто знає?». MCU може закінчитися перезавантаженням через правильну комбінацію подій (перерви спричинили, що в кінцевому підсумку скидання чіпа тощо), але немає гарантії того, що це станеться.

Як правило, велика ймовірність того, що це станеться, хоча, якщо це ввімкнено, - це внутрішній таймер (якщо такий існує) таймер часу та перезавантаження мікросхеми. Коли програма повністю пройде AWOL через подібний збій, інструкції щодо скидання таймера, як правило, не запускаються, тому він вичерпається та скидається.


Дякуємо за вашу відповідь, це чудовий підсумок ефектів. Можливо, я повинен був би зазначити, що мені хотілося б більше деталей щодо фактичного механізму цих пошкоджень: чи вся оперативна пам’ять виділяється на стек та купу, щоб покажчик стека перекидався та перезаписував попередні змінні / адреси? Або це менш залежить від відображення пам'яті кожного мікросхеми? За бажанням (це, мабуть, сама по собі тема), мені було б цікаво дізнатися, як реалізуються ці апаратні обробники.
Містер Містер

1
Це в основному залежить від компілятора та стандартної бібліотеки С, що використовується. Це також іноді залежить від налаштування компілятора (сценарії зв’язків тощо).
Маєнко

Чи можете ви розширити це, можливо, за допомогою декількох прикладів?
Містер Містер

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

12

Альтернативний вигляд: мікроконтролерам не вистачає пам'яті.

Принаймні, не при правильному програмуванні. Програмування мікроконтролера не зовсім схоже на програмування загального призначення, щоб правильно це зробити, ви повинні знати про його обмеження та відповідно програмувати. Існують інструменти, які допоможуть це забезпечити. Шукайте їх та вивчайте їх - принаймні, як читати сценарії та попередження про посилання.

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

Загальні правила програмування мікроконтролерів уникають цього: наприклад, вся пам'ять або виділяється на стеці, або виділяється статично (глобально); "новий" або "малок" заборонено. Так само є рекурсія, щоб максимальну глибину введення підпрограми можна було проаналізувати та показати, що вона вміщується у наявний стек.

Таким чином, максимальний необхідний обсяг пам’яті може бути обчислений під час компіляції або зв’язку програми та порівняння з розміром пам’яті (часто кодується у скрипті лінкера) для конкретного процесора, на який ви орієнтовані.

Тоді у мікроконтролера може не вистачити пам’яті, але програма може. І в такому випадку ти дістаєшся

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

Одним загальним набором правил програмування мікроконтролерів є MISRA-C , прийняті автомобільною промисловістю.

На мій погляд, найкраща практика - використовувати підмножину SPARK-2014 для Ада. Ада насправді орієнтована на невеликі контролери, такі як AVR, MSP430 та ARM Cortex, досить добре і, таким чином, забезпечує кращу модель для програмування мікроконтролерів, ніж C. Але SPARK додає до програми примітки у вигляді коментарів, де описано, що робить програма.

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

Незважаючи на те, що з SPARK працює більше передньої роботи, досвід показує, що вона може швидше та дешевше дістатися до продукту, оскільки ви не витрачаєте час на переслідування таємничих перезавантажень та іншої дивної поведінки.

Порівняння MISRA-C та SPARK


3
Поставте +1 до цього. Перенесення malloc()(і це супутник C ++ new) на AVR - це одне з найгірших речей, які могли зробити люди ардуїно, і призвело до багатьох, багатьох дуже плутаних програмістів зі зламаним кодом як на своєму форумі, так і обміну стеками ардуїно. Є дуже-дуже мало ситуацій, коли прийом mallocна ATmega вигідний.
Вонор Коннор

3
+1 для філософії, -1 для реалізму. Якщо речі були б запрограмовані належним чином, у цьому питанні не виникало б потреби. Питання було, що відбувається, коли у мікроконтролерів не вистачає пам'яті. Як не допустити, щоб їм не вистачало пам’яті - інше питання. На іншій ноті, рекурсія є потужним інструментом, як для вирішення проблем і бігти з стека.
PkP

2
@Brian, оскільки я не ідіот, я, очевидно, згоден з тобою. Мені просто подобається думати про це у зворотній точці зору - мені подобається сподіватися, що коли ти зрозумієш жахливі наслідки втрати пам’яті (стека), ти знайдеш способи запобігти цьому. Таким чином, у вас є реальний поштовх до пошуку належних практик програмування, а не просто дотримання належних порад щодо програмування ... і коли ви потрапите на бар'єр пам’яті, ви, швидше за все, застосуйте передовий досвід навіть за рахунок зручності. Це просто
оглядова

2
@PkP: чути голосно і чітко. Я назвав це альтернативним поглядом - адже він насправді не відповідає на питання!
Брайан Драммонд

2
@ MisterMystère: Мікроконтролерам зазвичай не вистачає пам'яті. Мікроконтролер, який має 4096 байт оперативної пам’яті при першому включенні, матиме 4096 байт назавжди. Можливо, що код може помилково спробувати отримати доступ до адрес, які не існують, або очікувати, що два різні способи обчислювальних адрес отримають доступ до різної пам'яті, коли цього не відбувається, але сам контролер просто виконає вказані інструкції.
supercat

6

Мені дуже подобається відповідь Маєнка, і я поставив це +1 собі. Але я хочу уточнити це до гострої точки:

Якщо мікроконтролеру не вистачає пам'яті, все може статися.

Ви дійсно не можете покластися ні на що, коли це станеться. Коли в машині не вистачає пам'яті стека, стек, швидше за все, пошкоджується. І як це станеться, може статися все, що завгодно. Змінні значення, розливи, часові регістри, всі стають пошкодженими, порушуючи потоки програми. Якщо / то / elses може оцінити неправильно. Зворотні адреси є зібраними, завдяки чому програма переходить до випадкових адрес. Будь-який код, написаний у програмі, може виконуватись. (Розгляньте код на зразок: "if [умова], то {fire_all_missles ();}"). Також цілий ряд інструкцій, які ви не написали, може виконати, коли ядро ​​переходить на непоєднане місце пам'яті. Усі ставки відключені.


2
Дякую за доповнення, мені особливо сподобався рядок fire_all_missles ().
Містер Містер

1

AVR має вектор скидання за нульовою адресою. Коли ви перезаписуєте стек з випадковим сміттям, ви зрештою обертаєтеся та записуєте якусь зворотну адресу, і це вказуватиме на "нікуди"; тоді, коли ви повернетеся з підпрограми до цього нікуди, виконання буде обведено навколо адреси 0, де зазвичай відбувається перехід на обробник скидання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.