今天我來講一講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文章
17123瀏覽量
350991 -
內存
+關注
關注
8文章
3019瀏覽量
74003 -
RTOS
+關注
關注
22文章
811瀏覽量
119595 -
SDK
+關注
關注
3文章
1035瀏覽量
45900 -
內存溢出
+關注
關注
0文章
10瀏覽量
1195
原文標題:三分鐘搞定MCU內存溢出
文章出處:【微信號:pzh_mcu,微信公眾號:痞子衡嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論