今天我來(lái)講一講MCU開(kāi)發(fā)中的一個(gè)棘手問(wèn)題——內(nèi)存溢出,希望能幫到遇到該問(wèn)題的同學(xué)們。
開(kāi)發(fā)環(huán)境
SDK版本:SDK_2_6_13_FRDM-KW38
SDK下載地址:https://mcuxpresso.nxp.com
開(kāi)發(fā)板:FRDM-KW38
IDE:IAR EmbeddedWorkbench for Arm version 8.50
演示代碼:https://github.com/N40E116/SDK_2_6_13_FRDM-KW38.git
本文總結(jié)了如下三類RAM使用情況的分析:
FreeRTOS RAM
CSTACK
動(dòng)態(tài)內(nèi)存分配
FreeRTOS RAM分析
因?yàn)槲覀兪褂玫氖菐?a href="http://srfitnesspt.com/tags/RTOS/" target="_blank">RTOS的工程,所以這里先介紹一下FreeRTOS里stack和heap的管理和分析。
Task Stack分析
每個(gè)task的stack是獨(dú)立分配的,我們使用IAR的FreeRTOS分析插件對(duì)stack進(jìn)行分析,打開(kāi)和使能方式如下:
以上方式針對(duì)的是在線debug時(shí)的分析查看,該方式查看信息較全面,可以在開(kāi)發(fā)階段根據(jù)多數(shù)場(chǎng)景分配合適的stack,但是對(duì)于debugger離線后的溢出檢測(cè)則需要使用FreeRTOS自帶的stack異常檢測(cè)工具,打開(kāi)方式如下,詳細(xì)信息請(qǐng)參考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通過(guò)如下宏定義,對(duì)于該Heap的溢出檢測(cè)可以使用FreeRTOS自帶的內(nèi)存分配失敗鉤子函數(shù)進(jìn)行檢測(cè)。
#define gTotalHeapSize_c 9000 #define configUSE_MALLOC_FAILED_HOOK 1
CSTACK分析
上面章節(jié)我們講了FreeRTOS中task占用stack的檢測(cè)方法,但是對(duì)于RTOS初始化前和中斷處理函數(shù)中用到的CSTACK該如何檢測(cè)呢?
IAR本身集成了CSTACK檢測(cè)功能,會(huì)顯示當(dāng)前棧的使用情況和最大棧深度,開(kāi)發(fā)階段連接debugger,按如下方式設(shè)置后即可查看CSTACK信息。
如果系統(tǒng)產(chǎn)生了CSTACK溢出,我們?cè)撊绾螜z測(cè)哪里產(chǎn)生了這個(gè)溢出呢?這時(shí)我們可以使用IAR的數(shù)據(jù)斷點(diǎn)功能,將棧底位置寫(xiě)入數(shù)據(jù)斷點(diǎn)的break位置,Access type改為Write,這樣只要棧底被修改了,即可產(chǎn)生斷點(diǎn),根據(jù)代碼break的位置,即可知道是哪里產(chǎn)生了CSTACK溢出。
一個(gè)快速獲得棧底位置的方法,如下圖所示,將鼠標(biāo)放到IAR的CSTACK的進(jìn)度條處即可顯示stack的使用范圍。
對(duì)于debugger離線后的CSTACK溢出檢測(cè),我們可以通過(guò)初始化棧空間為一個(gè)固定值,例如在線分析時(shí)為0xcd,定時(shí)檢測(cè)棧底上的該值是否有被修改來(lái)檢測(cè)。
如下所示為在idle任務(wù)中進(jìn)入低功耗前增加棧底數(shù)據(jù)的檢測(cè)。
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__;
動(dòng)態(tài)內(nèi)存分配
SDK沒(méi)有使用標(biāo)準(zhǔn)庫(kù)的malloc函數(shù),定義__heap_size__為0,所以用戶不能使用malloc和free函數(shù)。但如果需要?jiǎng)討B(tài)申請(qǐng)內(nèi)存該如何操作呢?SDK的Framework里定義了一套簡(jiǎn)化的內(nèi)存管理函數(shù)MEM_BufferAlloc()和MEM_BufferFree()。
配置文件中需要預(yù)先定義需要的數(shù)據(jù)塊大小和數(shù)量,內(nèi)存申請(qǐng)單元會(huì)從這些內(nèi)存塊中選取滿足大小要求的最小的數(shù)據(jù)塊作為MEM_BufferAlloc()的返回結(jié)果。
#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_
當(dāng)然如果用戶使用了該內(nèi)存分配方法,則需要根據(jù)應(yīng)用情況,對(duì)應(yīng)地增加內(nèi)存池中的系數(shù)。另外可以使能如下宏定義,查看分析內(nèi)存分配是否合理,具體用法請(qǐng)參考應(yīng)用筆記:
MemoryPool Optimizer on MKW3xA/KW3xZ (nxp.com.cn)。
MEM_DEBUG,MEM_TRACKING,MEM_DEBUG_OUT_OF_MEMORY
以上是我總結(jié)的一些overflow的應(yīng)對(duì)策略,強(qiáng)烈建議大家在開(kāi)發(fā)階段加上這些檢測(cè)措施,因?yàn)閮?nèi)存溢出會(huì)導(dǎo)致各種意想不到的結(jié)果,如果只跟著看到的異?,F(xiàn)象分析,往往會(huì)浪費(fèi)很多不必要的時(shí)間和精力,如果大家有其它應(yīng)對(duì)內(nèi)存溢出的方法,歡迎一起討論學(xué)習(xí)。
審核編輯:湯梓紅
-
mcu
+關(guān)注
關(guān)注
146文章
16802瀏覽量
349371 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
2947瀏覽量
73733 -
RTOS
+關(guān)注
關(guān)注
21文章
808瀏覽量
119296 -
SDK
+關(guān)注
關(guān)注
3文章
1015瀏覽量
45608 -
內(nèi)存溢出
+關(guān)注
關(guān)注
0文章
10瀏覽量
1187
原文標(biāo)題:三分鐘搞定MCU內(nèi)存溢出
文章出處:【微信號(hào):pzh_mcu,微信公眾號(hào):痞子衡嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論