Каким может быть раздел .data + .text = Сколько флэш-памяти будет потреблять конечная программа

Мне не удалось понять один момент, связанный с кодом Arduino.

Предположим, у меня есть следующий код

void setup() 
{
  Serial.begin(9600);
  Serial.println("foo");
}

void loop() 
{
   
}

Когда я компилирую приведенный выше код для Arduino Uno. Arduino IDE дает.

Sketch использует 1478 байт (4%) дискового пространства программы. Максимум 32256 байты. Глобальные переменные используют 192 байта (9%) динамической памяти, оставляя 1856 байт для локальных переменных. Максимум — 2048 байт.

Я проверяю сгенерированный файл с помощью avr-size.

.data                         26   8388864
.text                       1452         0
.bss                         166   8388890
.comment                      17         0
.note.gnu.avr.deviceinfo      64         0
.debug_aranges               208         0
.debug_info                 6150         0
.debug_abbrev               2721         0
.debug_line                 2239         0
.debug_frame                 504         0
.debug_str                  1770         0
.debug_loc                  1908         0
.debug_ranges                 80         0

Как видите, .data + .text = занимает место для хранения программы. Затем я проверил этот документ. https://courses.cs.washington.edu/courses/ csep567/04sp/pdfs/avr-libc-user-manual.pdf. Там говорится о avr-size

Note:
The avr-size program (part of binutils), coming from a Unix background,
doesn’t account for the .data initialization space added to the .text section, so in
order to know how much flash the final program will consume, one needs to add
the values for both, .text and .data (but not .bss), while the amount of pre-allocated
SRAM is the sum of .data and .bss.

Затем я проверяю содержимое раздела .data с помощью

avr-objdump.exe -s -j .data <my.elf>

Тогда я получил

Contents of section .data:
 800100 00000000 ff005f00 8c004c01 bd009b00  ......_...L.....
 800110 af00666f 6f000d0a 0000               ..foo.....

Я не понимаю, что на самом деле означает приведенная выше цитата? "фу"; хранится в разделе .data, но зачем мне добавлять этот раздел в раздел .text, чтобы подсчитать, сколько потребляет флэш-память?

, 👍2

Обсуждение

Как вы думаете, где хранятся эти строковые литералы?, @KIIV

@KIIV в SRAM ?, @Volkan Ünal

SRAM энергозависим, то есть не может ничего запомнить без питания., @KIIV


2 ответа


1

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

,

Вот почему F («макро») и перегруженные методы в ядре Arduino существуют, чтобы избежать этого «спама» в оперативной памяти. Однако Atmega4809 в этом не нуждается, поскольку вся память отображена в единое адресное пространство. Или платам на базе ARM это не нужно., @KIIV

@JRobert, интересное поведение. Значит все в сегменте .data ест из области флэша, да?, @Volkan Ünal

@embeddedstack, флэш-память в MCU — это «жесткий диск»., @Juraj

Да — сегмент .data содержит переменные, для которых вы указали начальные значения. Эти значения должны быть сохранены в энергонезависимой памяти для инициализации переменных в ОЗУ перед началом выполнения., @JRobert


4

Просто чтобы расширить то, что сказал Дж.Роберт: инициализируемые переменные и другие литералы (например, слово «foo» в вашем примере) должны откуда-то взяться и, таким образом, храниться в программной (флэш-памяти) и копироваться. в ОЗУ как часть инициализации C++.

Однако неинициализированные переменные не занимают флэш-память. Сравните:

char foo [1000];
void setup() 
{
  Serial.begin(9600);
  Serial.println("foo");
  Serial.print (foo);
}

void loop() 
{
}

Это отчет для Uno:

Sketch uses 1498 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.

Однако если вы инициализируете глобальную переменную foo следующим образом:

char foo [1000] = { 1, 2, 3, 4, 5 };
void setup() 
{
  Serial.begin(9600);
  Serial.println("foo");
  Serial.print (foo);
}

void loop() 
{ 
}

Теперь флеш занимает на 1000 байт больше:

Sketch uses 2498 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.

Это ясно показывает, что, поскольку неинициализированные переменные в глобальной области видимости установлены в ноль, компилятор сгенерировал код именно для этого. Однако инициализированные переменные необходимо скопировать из флэш-памяти в оперативную память.

Очень полезный метод при использовании этих чипов с небольшим объемом оперативной памяти — использовать букву «F». макрос, который фактически указывает компилятору копировать строки непосредственно из флэш-памяти, а не в ОЗУ, тем самым экономя пространство ОЗУ. Например:

Serial.println (F("Hello, world"));

В этом случае строка «Привет, мир» все равно должен храниться во флэш-памяти, но не копироваться в оперативную память, что позволяет экономить драгоценную память для других целей.


Запись инициализированных массивов во флеш

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

В этом примере кода показано, как можно хранить сообщения в PROGMEM (флэш-памяти) и извлекать их во время выполнения. Он использует pgm_read_word для чтения указателей, а затем strcpy_P для копирования строки из PROGMEM в ОЗУ, чтобы затем ее можно было распечатать.

const int NUMBER_OF_ELEMENTS = 10;

const char Message0000 [] PROGMEM = "Twas bryllyg, and ye slythy toves"; 
const char Message0001 [] PROGMEM = "Did gyre and gymble"; 
const char Message0002 [] PROGMEM = "in ye wabe:"; 
const char Message0003 [] PROGMEM = "All mimsy were ye borogoves; And ye mome raths outgrabe."; 
const char Message0004 [] PROGMEM = "\"Beware the Jabberwock, my son! \n The jaws that bite, the claws that catch!"; 
const char Message0005 [] PROGMEM = "Beware the Jubjub bird, and shun\n The frumious Bandersnatch!\""; 
const char Message0006 [] PROGMEM = "He took his "; 
const char Message0007 [] PROGMEM = "vorpal sword in hand:"; 
const char Message0008 [] PROGMEM = "Long time the manxome foe he sought - "; 
const char Message0009 [] PROGMEM = "So rested he by the Tumtum tree, \n And stood awhile in thought."; 

const char * const messages[NUMBER_OF_ELEMENTS] PROGMEM = 
   { 
   Message0000, 
   Message0001, 
   Message0002, 
   Message0003, 
   Message0004, 
   Message0005, 
   Message0006, 
   Message0007, 
   Message0008, 
   Message0009, 
   };

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  unsigned int count = 0;

  for (int i = 0; i < NUMBER_OF_ELEMENTS; i++)
    {
    char * ptr = (char *) pgm_read_word (&messages [i]);
    char buffer [80]; // должно быть достаточно большим!
    strcpy_P (buffer, ptr);
    Serial.println (buffer);
    count += strlen (buffer) + 1;
    }   // конец цикла for

  Serial.print (F("String memory used = "));
  Serial.println (count);

  }  // конец настройки

void loop () { } 

Этот конкретный пример не использует много оперативной памяти:

Sketch uses 2220 bytes (6%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
,

Спасибо за ваш вклад., @Volkan Ünal