Stm32 выделение памяти malloc

Обновлено: 07.07.2024

malloc Возвращает указатель типа void на выделенное пространство или в NULL случае нехватки доступной памяти. Чтобы получить указатель на тип, отличный от void , используйте приведение типа к возвращаемому значению. Дисковое пространство, на который указывает возвращаемое значение, будет гарантированно соответствовать требованиям к выравниванию для хранения объектов любого типа, если таковые требования не превышают базовые. (В Visual C++ основное выравнивание — это выравнивание, которое требуется для double , или 8 байт. В коде, ориентированном на 64-разрядные платформы, это 16 байт.) Используется _aligned_malloc для выделения хранилища для объектов с более высоким требованием выравнивания, например типов SSE __m128 и __m256 , и, объявленных с помощью __declspec(align( n )) WHERE, n больше 8. Если size значение равно 0, malloc выделяет элемент нулевой длины в куче и возвращает допустимый указатель на этот элемент. Всегда проверяйте возвращаемое значение malloc , даже если требуемый объем памяти мал.

Remarks

malloc Функция выделяет блок памяти как минимум size байтов. Блок может быть больше, чем size в байтах, из-за пространства, необходимого для выравнивания и сведений об обслуживании.

malloc Задает errno для значение, ENOMEM Если выделение памяти завершается ошибкой или объем запрошенной памяти превышает _HEAP_MAXREQ . Дополнительные сведения об этих и других кодах ошибок см. в статьях errno ,, _doserrno _sys_errlist и _sys_nerr .

Код запуска использует malloc для выделения хранилища _environ envp переменных, и argv . Следующие функции и их аналоги для расширенных символов также вызывают malloc .

Функция C++ _set_new_mode задает новый режим обработчика для malloc . Новый режим обработчика указывает, будет ли в случае сбоя malloc вызываться новая подпрограммы обработчика, заданная параметром _set_new_handler . По умолчанию не malloc вызывает новую подпрограммы обработчика при сбое выделения памяти. Это поведение по умолчанию можно переопределить таким образом, что при malloc сбое выделения памяти malloc вызывает новую подпрограммы обработчика таким же образом, как и new оператор, когда он завершается сбоем по той же причине. Чтобы переопределить значение по умолчанию, вызовите на _set_new_mode(1) раннем этапе программы или свяжите с NEWMODE.OBJ (см. раздел Параметры ссылки).

Если приложение связано с отладочной версией библиотек времени выполнения C, malloc разрешается в _malloc_dbg . Дополнительные сведения об управлении кучей в процессе отладки см. в разделе Сведения о куче отладки CRT.

malloc помечается __declspec(noalias) и __declspec(restrict) ; это означает, что функция гарантированно не изменяет глобальные переменные и что возвращаемый указатель не имеет псевдонима. Дополнительные сведения см. в разделах noalias и restrict .

По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это, см. раздел глобальное состояние в CRT.

[Учебник STM32H7] Глава 27 Реализация динамического выделения памяти TCM, SRAM и пяти блоков STM32H7

Учебное пособие в этой главе расскажет о динамической схеме управления памятью, которой могут управлять DTCM, SRAM1, SRAM2, SRAM3 и SRAM4, что имеет практическую ценность в практических проектах, таких как кодек MP3, кодек JPEG, видеоплеер, векторный шрифт Где требуется динамическая память.

27.1 Важные советы для начинающих

27.2 Миграция динамического управления памятью

27.3 Как использовать динамическую память

27.4 Объяснение экспериментальных процедур (MDK)

27.5 Экспериментальное рутинное описание (IAR)

27.1 Важные советы для начинающих

  1. Перед изучением этой главы вы должны сначала изучить Главу 25. Важно понять основы пяти областей памяти, таких как TCM и SRAM.
  2. Организовано динамическое управление памятью системы RTX5, и несколькими разделами можно управлять одновременно. Если используется в другой ОСРВ, не забудьте сделать взаимную защиту исключений или добавить блокировку расписания.
  3. Поддержка статистики динамического использования памяти.

27.2 Миграция динамического управления памятью

Миграция относительно проста, просто добавьте два файла в проект.

27.2.1 Портирование МДК

Обратите внимание, что на примере вспомогательного примера, приведенного в этой главе, путь этих двух файлов - \ User \ malloc.



Если какой-либо исходный файл должен использовать динамическую память, вы можете включить rtx_lib.h. Пример поддержки в этой главе - поместить его непосредственно в файл bsp.h. Какой исходный файл должен использовать динамическую память и напрямую включить заголовочный файл bsp.h может.

Через эти три простых шага трансплантация MDK завершена.

27.2.2. Порт IAR

Обратите внимание, что на примере вспомогательного примера, приведенного в этой главе, путь этих двух файлов - \ User \ malloc.



Если какой-либо исходный файл должен использовать динамическую память, вы можете включить rtx_lib.h. Пример поддержки в этой главе - поместить его непосредственно в файл bsp.h. Какой исходный файл должен использовать динамическую память и напрямую включить заголовочный файл bsp.h может.

Через эти три простых шага трансплантация IAR завершена.

27.3 Как использовать динамическую память

Далее в качестве примеров для иллюстрации используются MDK и IAR:

27.3.1. Динамическое использование памяти в MDK

  • Определить динамическую область памяти

Например, текущий DTCM для основной оперативной памяти мы можем напрямую определить большой массив как динамическое пространство памяти:

Если вы хотите использовать AXI SRAM в качестве динамического пространства памяти, вы можете использовать __attribute __ ((at ())) для указания адреса.

  • Инициализируйте область динамической памяти

Инициализируйте функцию osRtxMemoryInit, предоставляемую динамическим управлением памятью:

  • Подать заявку на динамическую память

Используйте функцию void * osRtxMemoryAlloc (void * mem, размер uint32_t, тип uint32_t), чтобы создать приложение с динамической памятью.

Для первого параметра введите первый адрес области памяти, например, если вы подаете заявку на AppMallocDTCM, заполните AppMallocDTCM.

Второй параметр - это размер приложения в байтах, в байтах.

Третий параметр может быть установлен в 0.

Возвращаемое значение является первым адресом примененного буфера. Если свободного места нет, возвращается NULL. Обратите на это особое внимание!

После подачи заявки на место, вы можете использовать его напрямую. Кроме того, обратите внимание на красную часть шрифта. Вы можете получить текущий размер пространства через DTCMUsed-> used и AXISRAMUsed-> used.

Используйте функцию uint32_t osRtxMemoryFree (void * mem, void * block) для динамического освобождения памяти.

Для первого параметра введите первый адрес области памяти, например, если вы отпустите AppMallocDTCM, заполните AppMallocDTCM.

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

Возвращаемое значение равно 1 для успеха и 0 для ошибки.

27.3.2. Динамическое использование памяти на IAR

Примечание: IAR использует это динамическое управление памятью, которое немного отличается от MDK в определении и такое же в других местах.

  • Определить динамическую область памяти

Например, текущий DTCM для основной оперативной памяти мы можем напрямую определить большой массив как динамическое пространство памяти:

Если вы хотите использовать AXI SRAM в качестве динамического пространства памяти, вы можете использовать __attribute __ ((at ())) для указания адреса.

  • Инициализируйте область динамической памяти

Инициализируйте функцию osRtxMemoryInit, предоставляемую динамическим управлением памятью:

  • Подать заявку на динамическую память

Используйте функцию void * osRtxMemoryAlloc (void * mem, размер uint32_t, тип uint32_t), чтобы создать приложение с динамической памятью.

Для первого параметра введите первый адрес области памяти, например, если вы подаете заявку на AppMallocDTCM, заполните AppMallocDTCM.

Второй параметр - это размер приложения в байтах, в байтах.

Третий параметр может быть установлен в 0.

Возвращаемое значение является первым адресом примененного буфера. Если свободного места нет, возвращается NULL. Обратите на это особое внимание!

После подачи заявки на место, вы можете использовать его напрямую. Кроме того, обратите внимание на красную часть шрифта. Вы можете получить текущий размер пространства через DTCMUsed-> used и AXISRAMUsed-> used.

Используйте функцию uint32_t osRtxMemoryFree (void * mem, void * block) для динамического освобождения памяти.

Для первого параметра введите первый адрес области памяти, например, если вы отпустите AppMallocDTCM, заполните AppMallocDTCM.

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

Возвращаемое значение равно 1 для успеха и 0 для ошибки.

27.4 Объяснение экспериментальных процедур (MDK)

Вспомогательные примеры:

V7-006_TCM, SRAM и другие пять реализации динамического выделения памяти памяти

Цель эксперимента:

  1. Изучите реализацию динамического распределения памяти TCM, SRAM и других пяти блоков.

Экспериментальное содержание:

  1. Запустите автоперезагрузку программного таймера 0 и переключайте LED2 каждые 100 мс.

Экспериментальная операция:

  1. Нажмите клавишу K1 и подайте заявку на получение 280 байтов, 64 байтов и 6111 байтов в последовательности от DTCM.
  2. Ключ K1 освобождается, освобождая пространство, запрашиваемое от DTCM.
  3. Нажмите клавишу K2 и подайте заявку на 160 байтов, 32 байта и 2333 байта в последовательности от SRAM AXI.
  4. Ключ K2 высвобождается, чтобы освободить пространство, запрошенное у AXI SRAM.
  5. Нажмите клавишу K3 и подайте заявку на получение 200 байтов, 96 байтов и 4111 байтов последовательно из SRAM домена D2.
  6. Ключ K3 освобождается для освобождения места, запрошенного от SRAM домена D2.
  7. Нажмите клавишу ОК джойстика, чтобы подать заявку на 300 байтов, 128 байтов и 5111 байтов последовательно из SRAM домена D3.
  8. Отпустите джойстик OK, чтобы освободить пространство, запрошенное у SRAM домена D3.

Информация, напечатанная на последовательном порту после включения питания:

Скорость передачи 115200, бит данных 8, бит четности отсутствует, стоп-бит 1


Дизайн программы:

Распределение размера стека системы:


RAMDTCM для космоса:


Аппаратная периферийная инициализация

Аппаратная периферия инициализируется в файле bsp.c:

Кеш данных и кеш инструкций включены.

Свойства MPU AXI SRAM:

Write back, Read allocate,Write allocate。

Атрибуты расширенного IO MPU для FMC:

Должно быть Устройство или Сильно Заказано.

Атрибуты MPU D2 SRAM1, SRAM2 и SRAM3:

Write through, read allocate,no write allocate。

Атрибуты MPU D3 SRAM4:

Write through, read allocate,no write allocate。

Основная функция:

Основная программа реализует следующие операции:

  • Запустите автоматическую переустановку программного таймера 0 и поверните светодиод 2 каждые 100 мс.
  • Когда нажата клавиша K1, из DTCM последовательно применяются 280 байтов, 64 байта и 6111 байт.
  • Клавиша K1 освобождается для освобождения места, запрашиваемого у DTCM.
  • Когда клавиша K2 нажата, 160 байтов, 32 байта и 2333 байта последовательно применяются из AXI SRAM.
  • Ключ K2 высвобождается, чтобы освободить пространство, запрошенное у AXI SRAM.
  • Когда клавиша K3 нажата, из SRAM домена D2 последовательно применяются 200 байтов, 96 байтов и 4111 байтов.
  • Ключ K3 высвобождается, чтобы освободить пространство, запрошенное от SRAM домена D2.
  • Нажмите клавишу ОК джойстика, чтобы подать заявку на 300 байтов, 128 байтов и 5111 байтов последовательно из SRAM домена D3.
  • Отпустите кнопку ОК на джойстике, чтобы освободить пространство, запрошенное у SRAM домена D3.

27.5 Экспериментальное рутинное описание (IAR)

Вспомогательные примеры:

V7-006_TCM, SRAM и другие пять реализации динамического выделения памяти памяти

Цель эксперимента:

  1. Изучите реализацию динамического распределения памяти TCM, SRAM и других пяти блоков.

Экспериментальное содержание:

  1. Запустите автоперезагрузку программного таймера 0 и переключайте LED2 каждые 100 мс.

Экспериментальная операция:

  1. Нажмите клавишу K1 и подайте заявку на получение 280 байтов, 64 байтов и 6111 байтов в последовательности от DTCM.
  2. Ключ K1 освобождается, освобождая пространство, запрашиваемое от DTCM.
  3. Нажмите клавишу K2 и подайте заявку на 160 байтов, 32 байта и 2333 байта в последовательности от SRAM AXI.
  4. Ключ K2 высвобождается, чтобы освободить пространство, запрошенное у AXI SRAM.
  5. Нажмите клавишу K3 и подайте заявку на получение 200 байтов, 96 байтов и 4111 байтов последовательно из SRAM домена D2.
  6. Ключ K3 освобождается для освобождения места, запрошенного от SRAM домена D2.
  7. Нажмите клавишу ОК джойстика, чтобы подать заявку на 300 байтов, 128 байтов и 5111 байтов последовательно из SRAM домена D3.
  8. Отпустите джойстик OK, чтобы освободить пространство, запрошенное у SRAM домена D3.

Информация, напечатанная на последовательном порту после включения питания:

Скорость передачи 115200, бит данных 8, бит четности отсутствует, стоп-бит 1


Дизайн программы:

Распределение размера стека системы:


RAMDTCM для космоса:


Аппаратная периферийная инициализация

Аппаратная периферия инициализируется в файле bsp.c:

MPUКонфигурация и конфигурация кеша:

Кеш данных и кеш инструкций включены.

Свойства MPU AXI SRAM:

Write back, Read allocate,Write allocate。

Атрибуты расширенного IO MPU для FMC:

Должно быть Устройство или Сильно Заказано.

Атрибуты MPU D2 SRAM1, SRAM2 и SRAM3:

Write through, read allocate,no write allocate。

Атрибуты MPU D3 SRAM4:

Write through, read allocate,no write allocate。

Основная функция:

Основная программа реализует следующие операции:

  • Запустите автоматическую переустановку программного таймера 0 и поверните светодиод 2 каждые 100 мс.
  • Когда нажата клавиша K1, из DTCM последовательно применяются 280 байтов, 64 байта и 6111 байт.
  • Клавиша K1 освобождается для освобождения места, запрашиваемого у DTCM.
  • Когда клавиша K2 нажата, 160 байтов, 32 байта и 2333 байта последовательно применяются из AXI SRAM.
  • Ключ K2 высвобождается, чтобы освободить пространство, запрошенное у AXI SRAM.
  • Когда клавиша K3 нажата, из SRAM домена D2 последовательно применяются 200 байтов, 96 байтов и 4111 байтов.
  • Ключ K3 высвобождается, чтобы освободить пространство, запрошенное от SRAM домена D2.
  • Нажмите клавишу ОК джойстика, чтобы подать заявку на 300 байтов, 128 байтов и 5111 байтов последовательно из SRAM домена D3.
  • Отпустите кнопку ОК на джойстике, чтобы освободить пространство, запрошенное у SRAM домена D3.

27.6 Резюме

Эта глава многое объясняет для всех, но она более практична для проектов, особенно кодеков MP3, кодеков JPEG, видеоплееров, векторных шрифтов и других случаев, когда требуется динамическая память.

Интеллектуальная рекомендация

совместный запрос mysql с тремя таблицами (таблица сотрудников, таблица отделов, таблица зарплат)

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


[Загрузчик классов обучения JVM] Третий день пользовательского контента, связанного с загрузчиком классов


IP, сеанс и cookie

В настоящее время я работаю над встроенным проектом (STM32F103RB, CooCox CoIDE v.1.7.6 с arm-none-eabi-gcc 4.8 2013q4), и я пытаюсь понять, как malloc() ведет себя на простой C когда ОЗУ заполнено.

Мой STM32 имеет 20kB = 0x5000Bytes ОЗУ, 0x200 используются для стека.

Я бы ожидал, что malloc() вернет NULL , как только куча будет слишком мала для выделения:

0x5000 (ОЗУ) - 0x83C (bss) - 0x200 (стек) = 0x45C4 (куча)

Есть ли у кого-нибудь совет, как прогнозировать сбой malloc() , так как ожидание возврата NULL не работает.

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

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

Например, скажем, вы попросили 10-байтовый кусок. malloc будет захватывать доступный 16-байтовый фрагмент, скажем, по адресам 0x3200..0x320F , записать длину (например, 16) в байты 1 и 2 и вернуть 0x3202 к вам. Теперь ваша программа может использовать десять байт от 0x3202 до 0x320B . Остальные четыре байта также доступны - если вы вызываете realloc и запрашиваете 14 байтов, перераспределения не будет.

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

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

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

Например (это также мое предположение о том, что происходит в вашей системе):

Ваша куча, скорее всего, начинается сразу после стека. Теперь предположим, что у вас есть переполнение стека в main . Тогда одна из операций, которые вы выполняете в main , которая, естественно, является юридической операцией, насколько это возможно, переопределяет начало кучи некоторыми "мусорными" данными.

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

Итак, для начала я настоятельно рекомендую увеличить размер стека от 0x200 байт до 0x400 байт. Обычно это определяется в файле компоновщика ссылок или через IDE в настройках компоновщика проекта.

Если ваш проект включен в IAR, вы можете изменить его в файле icf :

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

В файле "startup_stm32f03xx.s" убедитесь, что у вас есть следующий фрагмент кода:

Затем в том же файле добавьте следующий обработчик прерываний (где находятся все остальные обработчики):

Затем в файле 'stm32f03xx.c' добавьте следующий ISR:

Если вы не можете использовать printf в момент выполнения, когда это конкретное прерывание с жестким замыканием происходит, сохраните все вышеперечисленные данные в глобальном буфере вместо этого, чтобы вы могли просмотреть его после достижения while (1) .

UPDATE:

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

У меня когда-то был случай, когда базовый адрес кучи был установлен на постоянный адрес, что было хорошо для начала. Но затем я постепенно увеличивал размер раздела данных, добавляя в программу глобальные переменные. Стек был расположен сразу после раздела данных, и он "продвигался вперед" по мере того, как секция данных увеличивалась, поэтому никаких проблем ни с одним из них не возникало. Но в итоге куча была выделена "поверх" части стека. Поэтому в какой-то момент кучи-операции начали переопределять переменные в стеке, а операции стека стали переопределять содержимое кучи.

Я должен сделать 64-битный стек. Чтобы мне было удобно работать с malloc, мне удалось записать в память два целых числа (32 бита) и прочитать оттуда:

32bits

Но, когда я пытаюсь сделать это с 64 бит:

64bits

спросил(а) 2016-12-12T18:12:00+03:00 4 года, 11 месяцев назад

Первый фрагмент кода работает отлично. Как предложил Джестер, вы пишете 64-битное значение в двух отдельных (32-разрядных) половинах. Так вы должны это сделать в 32-битной архитектуре. У вас нет 64-битных регистров, и вы не можете сразу записать 64-битные куски памяти. Но вы уже, казалось, знали это, поэтому я не буду его преследовать.

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

64-разрядные регистры Rxx вместо Exx . Когда вы используете QWORD PTR , вы захотите использовать Rxx ; когда вы используете DWORD PTR , вы захотите использовать Exx . Оба являются законными в 64-битном коде, но только 32-разрядные DWORD являются законными в 32-битном коде.

Несколько других замечаний:

Несмотря на то, что для регистрации регистра с использованием MOV xxx, 0 совершенно очевидно, что он меньше и быстрее использует XOR eax, eax , так что это вообще то, что вы должны писать. Это очень старый трюк, который должен знать любой программист на ассемблере, и если вы когда-нибудь попробуете читать программы сборки других людей, вам нужно быть знакомым с этой идиомой. (Но на самом деле, в коде, который вы пишете, вам не нужно это делать вообще. По этой причине см. Пункт № 2.)

В 64-битном режиме все инструкции неявно обнуляют верхние 32 бита при записи младших 32 бит, поэтому вы можете просто написать XOR eax, eax вместо XOR rax, rax . Это снова и снова и снова.

Вызывающее соглашение для 64-битных программ отличается от того, которое используется в 32-битных программах. Точная спецификация соглашения о вызове будет меняться в зависимости от используемой операционной системы. Как прокомментировал Питер Кордес, информация об этом содержится в вики-теге x86. Соглашения о вызовах Windows и Linux x64 передают по крайней мере первые 4 целочисленных параметра в регистрах (а не в стеке, таком как соглашение о вызове x86-32), но фактически используемые регистры разные. Кроме того, соглашения с 64-битными вызовами имеют разные требования, чем 32-битные соглашения о вызовах для того, как вы должны настроить стек перед вызовом функций.

(Так как ваш скриншот говорит что-то о "MASM", я предполагаю, что вы используете Windows в приведенном ниже примере кода.)

Если бы вы хотели сделать это в 32-битных половинах, даже в 64-битной архитектуре, вы, безусловно, могли бы. Это будет выглядеть следующим образом:

Обратите внимание, что эти две последние инструкции MOV идентичны тем, что вы написали в 32-битной версии кода. Это имеет смысл, потому что вы делаете то же самое.

Причина, по которой код, который вы изначально писали, не работает, заключается в том, что EAX не содержит QWORD PTR , он содержит DWORD PTR . Следовательно, ассемблер генерировал ошибку "неверные инструкции", потому что было несоответствие. Это по той же причине, что вы не компенсируете 8, потому что DWORD PTR составляет всего 4 байта. QWORD PTR действительно 8 байтов, но у вас нет одного из них в EAX .

Или, если вы хотите записать 16 байт:

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

Читайте также: