1. 程式人生 > >【堆疊和棧溢位】MSP430 陣列填充越界引起的棧溢位 導致程式跑飛

【堆疊和棧溢位】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;

}