【堆疊和棧溢位】MSP430 陣列填充越界引起的棧溢位 導致程式跑飛
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
2、堆區(heap) — 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列。
//main.cpp
int a = 0; 全域性初始化區
char *p1; 全域性未初始化區
main()
{
int b; 棧
char s[] = "abc"; 棧
char *p2; 棧
char *p3 = "123456"; 123456\0在常量區,p3在棧上。
static int c =0; 全域性(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20位元組的區域就在堆區。
strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
}
二、堆和棧的理論知識
2.1申請方式
stack:
由系統自動分配。 例如,宣告在函式中一個區域性變數 int b; 系統自動在棧中為b開闢空間
heap:
需要程式設計師自己申請,並指明大小,在c中malloc函式
如p1 = (char *)malloc(10);
在C++中用new運算子
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在棧中的。
12月3日 MSP430 堆疊溢位 解決
MSP430程式在執行的過程中,出現宕機的現象,通過IAR編譯器觀察,宕機的原因是棧溢位。
因為定義的區域性變數是在棧內的,所以分析可能是區域性變數導致棧溢位,最有可能導致編譯器不能事先判斷
使用了多少棧的(超出設定值會報警),就是程式中產生新的佔用棧的變數,由之前的經驗推測是陣列越界,因為如果程式不顧陣列的邊界,越界不斷的往裡填東西,越界的部分就會被當成區域性變數,佔用棧的記憶體。
因為棧是從RAM的底部網上長(存資料)的,而其他程式執行的資料是從頂部往下的,所以當棧越存越多,越積越高的時候,棧就會和程式執行時的資料碰頭,二者佔滿整個RAM記憶體,此時棧再繼續消耗,棧再向上長,直接覆蓋掉程式執行時所需的變數,程式就要跑飛了。
(iar430中定義的變數是從ram的起始地址向上(由小到大),而堆疊是從ram的終止地址向下(由大到小)。
以msp430F5438為例,它是16KB的ram,起始地址為0x1C00h,終止地址為005BFFh(data sheet
15頁),所以它的變數是從0x1C00h開始,向0x005BFFh方向存放,而堆疊是從0x005BFFh開始,向0x1C00h方向壓棧。當變數儲存空間和堆疊最大佔用空間在中間相遇時,就發生了堆疊溢位。)
下面就詳細介紹如何檢視ram使用情況:
1 當然是燒程式到目標板裡呀
2 選擇view/memory,開啟memory視窗
3 從ram的起始地址0x1C00h開始,輸入0x3fff(16KB),再回車
4 選中0x1C00h~0x005BFFh區域,右鍵選擇memory fill……
5 在memory fill中的start寫入:0x1C00h,length寫:0x3fff,value填入FF(也可填入其他值),被選中的區域全填充FF
6 執行程式,跑一遍設計的所有功能,再停止cspy,看看memory視窗
7 如果再填充的區域內已經沒有FF存在,就說明已經發生堆疊溢位或是會有溢位的危險(ram剛好夠用)。最好保留一定餘量的ram不被改變,以防發生溢位
驗證:定義一個UART0_RX_HEX_BUF[400];
在程式中加入這個程式碼。
unsigned int i;
for(i=0;i<17000;i++)
因為MSP430的RAM只有16KB,所以填17000次(char)足以保證棧全部佔滿RAM,覆蓋掉所以執行程式的變數,導致跑飛。
程式執行結果是,程式跑飛。產看棧STACK,棧溢位。假設應該是正確的。
因而分析導致棧溢位的程式段應該是這裡:
紅色部分當時是為了避免中斷程式過長跟不上中斷,使得資料收不全而去掉的,現在分析if(UART0_RX_HEX_Len >= UART0_RX_HEX_MAXLen)
UART0_RX_HEX_Len = 0;
這兩句子不能去掉,因為,在沒有接收到協議資料(配置資料,FE……FA)之前,UART0_RX_HEX_BUF和UART0_RX_BUF一樣在接收返回的AT指令返回,陣列在不斷的填裝,UART0_RX_BUF每隔一段時間就會被讀取一次,並清空,因而不會出現溢位。但是在沒有收到配置資料之前,UART0_RX_HEX_BUF不會清空,且在配置完之後,只要服務站不發協議資料,也不會清空,因而UART0_RX_HEX_BUF基本處於只填裝不清空的狀態,最後遠遠越界,最終導致開頭分析的結果。
加了清零語句:
if(UART0_RX_HEX_Len >= UART0_RX_HEX_MAXLen)
UART0_RX_HEX_Len = 0;
後,宕機在12小時內在沒有出現,其他比對機器全部宕機,初步證明分析應該是正確的。
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR()
{
UCA3TXBUF=UCA0RXBUF;//把接收的資料發到U3便於觀察(LCX注)
//接收字元資料時
UART0_RX_BUF[UART0_RX_Len] = UCA0RXBUF;
UART0_RX_Len++;
ReadUART0_new=Read_Cycle;
//接收協議資料時(16進位制)
UART0_RX_HEX_BUF[UART0_RX_HEX_Len] = UCA0RXBUF;
if((UART0_RX_HEX_BUF[UART0_RX_HEX_Len]==0xFF)&&(UART0_RX_HEX_BUF[UART0_RX_HEX_Len-1]==0xFE))
{
UART0_RX_HEX_BUF[0]=UART0_RX_HEX_BUF[UART0_RX_HEX_Len-1];
UART0_RX_HEX_BUF[1]=UART0_RX_HEX_BUF[UART0_RX_HEX_Len];
UART0_RX_HEX_Len=1;
}
else if((UART0_RX_HEX_BUF[UART0_RX_HEX_Len]==0xFA)&&(UART0_RX_HEX_BUF[UART0_RX_HEX_Len-1]==0xFF))
{
ReadUART0_Permit=1;
UCA0IE &= ~ UCRXIE;
}
UART0_RX_HEX_Len++;
if(UART0_RX_HEX_Len >= UART0_RX_HEX_MAXLen)
UART0_RX_HEX_Len = 0;
}