在许多嵌入式应用程序中,内存分配必须是静态的,而不是动态的。意味着在应用程序中不应使用对malloc()或free()等内容的调用,因为它们可能会在运行时失败(内存不足、堆碎片)。
但是,当与第三方库甚至 C/C++ 标准库链接时,如何确保不使用动态内存?对于C++对象或对 printf()的简单调用,也可能会出现此问题,该调用需要在内部分配一些动态内存。
1、零堆大小?
一种简单(而且相当幼稚)的方法是将堆大小设置为零。大多数链接器脚本或链接器环境都能够将堆大小设置为零,如下所示:
但是,这无济于事,因为大多数链接器脚本只使用堆栈空间和变量 (bss) 之间的可用空间: 因此,将堆大小设置为零只是假设保留为零,但库的 _sbrk()将继续使用动态内存,可能会崩溃到堆栈空间。
底线:大多数链接器脚本堆大小设置为零将无济于事。它甚至增加了堆崩溃到堆栈中的可能性,因为链接器假定堆的使用量为零。但它不会阻止使用堆。
2、堆在链接器文件中
这可以从下面显示的链接器文件中看出:
若要确保不使用堆,最简单的方法是简单地删除上述链接器文件中的 .heap 分配。检查链接器文件中是否有“堆”或类似内容,并确保没有为其分配内存。特别是确保没有使用符号“__prvHeapStart”或“__prvHeapLimit”,因为它们通常是newlib/newlib-nano库动态内存分配所必需的。
3、链接器自由标记脚本
恩智浦MCUXpresso IDE等某些环境正在使用自动生成的链接器文件。但是,如果您了解它使用的FreeMarker脚本,则很容易处理此问题。脚本位于
<MCUXpresso IDE Installation Path>\ide\Wizards\linker
所以我可以使用修改后的 FreeMarker 脚本轻松禁用 .heap:
4、__sbrk()
删除堆及其符号后,malloc()的任何用法都应该给出一个链接器错误,类似于这个错误:
Invoking: MCU Linker
arm-none-eabi-gcc -nostdlib -Xlinker -Map="AEMBS_tinyK22_HS22.map" -Xlinker --gc-sections -Xlinker -print-memory-usage -Xlinker --sort-section=alignment -Xlinker --cref -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -T "AEMBS_tinyK22_HS22_Debug.ld" -o "AEMBS_tinyK22_HS22.axf" ./utilities/fsl_assert.o ... ./McuLib/FreeRTOS/FreeRTOShooks.o
arm-none-eabi/bin/ld.exe: arm-none-eabi/lib/thumb/v7e-m+fp/hard\libcr_newlib_semihost.a(_cr_sbrk.o): in function `_sbrk':
_cr_sbrk.c:(.text._sbrk+0x38): undefined reference to `_pvHeapStart'
5、链接器交叉引用
如果不清楚使用内存分配或导致错误的原因,可以使用链接器交叉引用。为此,必须将 GNU 链接器 –cref 选项添加到链接器选项中:
这会在链接器 .map 文件中生成如下所示的信息:
Cross Reference TableSymbol File
ADC0_DriverIRQHandler ./startup/startup_mk22f51212.o
ADC0_IRQHandler ./startup/startup_mk22f51212.o
ADC16_ClearStatusFlags ./drivers/fsl_adc16.o
ADC16_Deinit ./drivers/fsl_adc16.o
...
main ./source/main.o./startup/startup_mk22f51212.o
malloc arm-none-eabi/lib/thumb/v7e-m+fp/hard\libc_nano.a(lib_a-malloc.o)arm-none-eabi/lib/thumb/v7e-m+fp/hard\libc_nano.a(lib_a-rand.o)./drivers/fsl_common.o
mcgConfig_BOARD_BootClockRUN ./board/clock_config.o
这给出了使用 malloc()之类的模块列表。
6、图像信息
找出依赖关系的另一种方法是在 Eclipse/MCUXpresso 中使用“图像信息”视图,请参阅在构建后操作中列出具有 GNU 大小实用程序的所有文件的代码和数据大小:
7、免费实时操作系统静态内存分配
更多旁注:默认情况下,FreeRTOS 使用自己的动态堆分配。但是可以静态方式运行 RTOS(无动态内存分配),请参阅带静态内存分配的 FreeRTOS V9.0.0。
8、总结
许多应用程序不需要动态内存分配和使用。为了防止使用 malloc()和 free(),最好删除链接器文件中的任何堆定义,以导致链接器错误。那么 GNU 链接器交叉引用表或图像信息可能非常有用。
9、链接
- 使用 FreeMarker 脚本的示例:教程:将 BLE+NRF Kinetis Design Studio Project 移植到 MCUXpresso IDE
- 带有 FreeMarker 脚本的引导加载程序:将引导加载程序应用程序与 Eclipse 和 FreeMarker 脚本链接起来
- 自由标记脚本:https://freemarker.apache.org/