Статическое Распределение Памяти В Микроконтроллерах.

Холмс: Дорогая, не могли бы вы сказать мне, где мы? Пастух: Вы на воздушном шаре!!! Холмс: Вы, должно быть, программист. Пастух: Да, но как ты догадался? Холмс: Только программист мог дать такую точную и такой бесполезный ответ. .

отрывок из известного анекдота Если вы когда-либо программировали для микроконтроллера, используя Arduino IDE или работая напрямую с компилятором для AVR, ARM или ESP, вы, вероятно, видели отчеты о завершении сборки, такие как

  
  
  
  
  
   

Sketch uses 1,090 bytes (3%) of program storage space. Maximum is 30,720 bytes. Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.

Или

text data bss dec hex filename 52136 1148 12076 65360 ff50 MyProject

Такие отчеты действительно абсолютно точны.

Просто они неполны, а потому не так уж и полезны.

Проблема в том, что учитываются только те данные, которые были распределены статически.

Но все, что выделяется через new или malloc, в статистику не включается.

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

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

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

Действительно оправдано.

Как правило, это выделение какого-то буфера или создание каких-то объектов в самом начале прошивки, после чего эти объекты висят в памяти до следующего сброса.

И это повод выделить такую память статически — этим мы сегодня и займемся.

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

Идти.



Статическое распределение переменных

Если не вдаваться глубоко в детали и описания различных сегментов данных, то оперативную память в микроконтроллере можно разделить на 3 типа:
  • статически распределенная память — сюда попадает все, о чем знает компилятор на этапе сборки проекта: глобальные переменные и объекты, статические переменные в функциях и объектах
  • куча — это большая область памяти, из которой система (распределитель памяти) отрезает кусочки столько, сколько необходимо.

    Такие функции, как malloc и оператор new, берут память отсюда.

  • Стек — это место, где процессор хранит регистры и адреса возврата из функций, но в стек также помещаются локальные переменные функций.

Давайте исследуем это на практике.

Чтобы не беспокоиться о компиляторе и системе сборки, я буду использовать Arduino IDE для целевой платформы Arduino Nano. Давайте еще раз посмотрим на отчет сверху (из шапки).

Как я уже говорил, в статистику попадают только те объекты, которые были распределены на этапе компиляции.



Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.

Судя по сообщению, у нас просто много свободной памяти.

Но давайте посмотрим на код

int * buf; void setup() { buf = new int[3000]; } void loop() {}

Прошивка пытается выделить больше памяти, чем доступно в самом микроконтроллере.

Конечно, такой код не будет работать.

Но по моему мнению.

Самая большая проблема здесь в том, что код компилируется успешно , но компилятор вовремя не ударил по рукам и не предупредил, что может закончиться память.

И хорошо, если есть понятный обработчик ошибок.

А если нет? Конечно, вы сейчас скажете, что он злой Буратино! В отчете сообщается, что имеется всего 2 КБ, а вы пытаетесь выделить 3000 элементов по 2 байта каждый.

Но давайте подумаем, как это будет выглядеть в реальном проекте.

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

И тут вдруг происходит какое-то редкое событие, требующее еще одного кусочка памяти и.

ох, все, мы приехали.

Что я подразумеваю под статическим распределением и чего именно я хочу достичь? Я ничего не имею против динамического распределения памяти.

Но я не очень понимаю, зачем использовать динамическое распределение для объектов, буферов и массивов заранее известного размера.

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



int buf[300]; void setup() { buf[0] = 1; //Avoid optimizing this array } void loop() { }

Теперь компилятор и компоновщик заранее знают о неком массиве размером 300*2=600 байт, который необходимо разместить в оперативной памяти.

Более того, компоновщик может выделить этому массиву фиксированный адрес, который при желании можно просмотреть с помощью утилиты objdump (если, конечно, вы найдете, куда Arduino IDE помещает бинарник)

cd C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_55567 "C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objdump.exe" -x -D -S -s StaticMem.ino.elf … 00800100 l

Теги: #Программирование микроконтроллеров #C++ #Разработка для Arduino #статическое выделение #статическое выделение #статическое выделение памяти

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.