今天我来讲一讲MCU开发中的一个棘手问题——内存溢出,希望能帮到遇到该问题的同学们。
开发环境
SDK版本:SDK_2_6_13_FRDM-KW38
SDK下载地址:https://mcuxpresso.nxp.com
开发板:FRDM-KW38
IDE:IAR EmbeddedWorkbench for Arm version 8.50
演示代码:https://github.com/N40E116/SDK_2_6_13_FRDM-KW38.git
本文总结了如下三类RAM使用情况的分析:
FreeRTOS RAM
CSTACK
动态内存分配
FreeRTOS RAM分析
因为我们使用的是带RTOS的工程,所以这里先介绍一下FreeRTOS里stack和heap的管理和分析。
Task Stack分析
每个task的stack是独立分配的,我们使用IAR的FreeRTOS分析插件对stack进行分析,打开和使能方式如下:
以上方式针对的是在线debug时的分析查看,该方式查看信息较全面,可以在开发阶段根据多数场景分配合适的stack,但是对于debugger离线后的溢出检测则需要使用FreeRTOS自带的stack异常检测工具,打开方式如下,详细信息请参考FreeRTOS- stacks and stack overflow checking
#define configCHECK_FOR_STACK_OVERFLOW 2 #if (configCHECK_FOR_STACK_OVERFLOW != 0) void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { panic(0,(uint32_t)vApplicationStackOverflowHook,0,0); } #endif
FreeRTOS Heap分析
FreeRTOS使用的heap通过如下宏定义,对于该Heap的溢出检测可以使用FreeRTOS自带的内存分配失败钩子函数进行检测。
#define gTotalHeapSize_c 9000 #define configUSE_MALLOC_FAILED_HOOK 1
CSTACK分析
上面章节我们讲了FreeRTOS中task占用stack的检测方法,但是对于RTOS初始化前和中断处理函数中用到的CSTACK该如何检测呢?
IAR本身集成了CSTACK检测功能,会显示当前栈的使用情况和最大栈深度,开发阶段连接debugger,按如下方式设置后即可查看CSTACK信息。
如果系统产生了CSTACK溢出,我们该如何检测哪里产生了这个溢出呢?这时我们可以使用IAR的数据断点功能,将栈底位置写入数据断点的break位置,Access type改为Write,这样只要栈底被修改了,即可产生断点,根据代码break的位置,即可知道是哪里产生了CSTACK溢出。
一个快速获得栈底位置的方法,如下图所示,将鼠标放到IAR的CSTACK的进度条处即可显示stack的使用范围。
对于debugger离线后的CSTACK溢出检测,我们可以通过初始化栈空间为一个固定值,例如在线分析时为0xcd,定时检测栈底上的该值是否有被修改来检测。
如下所示为在idle任务中进入低功耗前增加栈底数据的检测。
void check_overflow_cstack() { extern uint32_t CHECK_OVERFLOW_CSTACK_SIZE[]; uint32_t CHECK_OVERFLOW_CSTACK_END = *((uint32_t*)0UL) - (uint32_t)CHECK_OVERFLOW_CSTACK_SIZE; if(*(uint32_t*)CHECK_OVERFLOW_CSTACK_END != 0xcdcdcdcd) { panic(0,(uint32_t)check_overflow_cstack,0,0); } } void BOARD_EnterLowPowerCb(void) { check_overflow_cstack(); … }
另外链接文件MKW38A512xxx4_PD_connectivity_lp.icf需要增加如下定义:
define exported symbol CHECK_OVERFLOW_CSTACK_SIZE = __size_cstack__;
动态内存分配
SDK没有使用标准库的malloc函数,定义__heap_size__为0,所以用户不能使用malloc和free函数。但如果需要动态申请内存该如何操作呢?SDK的Framework里定义了一套简化的内存管理函数MEM_BufferAlloc()和MEM_BufferFree()。
配置文件中需要预先定义需要的数据块大小和数量,内存申请单元会从这些内存块中选取满足大小要求的最小的数据块作为MEM_BufferAlloc()的返回结果。
#define AppPoolsDetails_c _block_size_ 80 _number_of_blocks_ 7 _eol_ _block_size_ 248 _number_of_blocks_ 2 _eol_ _block_size_ 312 _number_of_blocks_ 1 _eol_ _block_size_ 392 _number_of_blocks_ 1 _eol_
当然如果用户使用了该内存分配方法,则需要根据应用情况,对应地增加内存池中的系数。另外可以使能如下宏定义,查看分析内存分配是否合理,具体用法请参考应用笔记:
MemoryPool Optimizer on MKW3xA/KW3xZ (nxp.com.cn)。
MEM_DEBUG,MEM_TRACKING,MEM_DEBUG_OUT_OF_MEMORY
以上是我总结的一些overflow的应对策略,强烈建议大家在开发阶段加上这些检测措施,因为内存溢出会导致各种意想不到的结果,如果只跟着看到的异常现象分析,往往会浪费很多不必要的时间和精力,如果大家有其它应对内存溢出的方法,欢迎一起讨论学习。
审核编辑:汤梓红
-
mcu
+关注
关注
146文章
16840浏览量
349724 -
内存
+关注
关注
8文章
2957浏览量
73781 -
RTOS
+关注
关注
21文章
809浏览量
119338 -
SDK
+关注
关注
3文章
1017浏览量
45658 -
内存溢出
+关注
关注
0文章
10浏览量
1189
原文标题:三分钟搞定MCU内存溢出
文章出处:【微信号:pzh_mcu,微信公众号:痞子衡嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论