Nuvoton Keil技巧篇4 -- 堆和堆疊

關鍵字 :新唐科技KeilSRAM堆疊堆積

        工程師們在進行軟體編寫時,經常會遇到莫名其妙的系統當機,或者出現Hardfault錯誤訊息,查了半天程式碼也找不到原因,最後才發現是因為堆疊溢出了。我這邊整理了一些關於堆和堆疊的個人見解,希望能在程式設計時避免踩到這些大坑,若有錯誤的地方也歡迎指正。
        RAM 空間:分成 .data、.bss、堆 heap 和堆疊 stack。

 

 

.data 段:初始化的全域/靜態變數

.bss 段:未初始化的全域/靜態變數

堆(heap):malloc() 函數動態分配的記憶體屬於堆的空間,執行時從 RAM 的低位址向上增長。在單晶片啟動檔案中也有對堆大小(Heap Size)的定義。
例如,新唐M031範例程式的啟動文件中,Heap Size(堆積大小)被設置為0,需要使用時需自行設定大小。

堆疊(stack):用來存放函式的區域變數、函式的形參、函式的返回位址、中斷上下文位址(中斷嵌套從內部開始計算)等,從指定RAM區域的高位址向下增長。因此,我們在函式中的區域變數、陣列等不能超過堆疊的大小(包含嵌套的函式),否則程式會崩潰並進入HardFault‌。

當一個函式被呼叫時,系統會在堆疊上為該函式分配一塊連續的空間,用來存放這些臨時變數。當函式執行完畢,這塊空間會自動釋放。堆疊的記憶體分配和釋放是由編譯器自動完成的,遵循後進先出(LIFO)的原則。每當有新的函式呼叫發生時,堆疊指標會向下移動,為新的區域變數和參數分配空間;函式返回時,堆疊指標會向上移動,回收之前分配的空間。

例如,新唐 M031 範例程式的 startup 文件中,Stack Size 設定為 0x200,如果需要使用更大的堆疊空間,可以自行修改。

 

 

堆疊溢出是導致系統崩潰、當機、行為異常甚至安全漏洞的常見原因。堆疊空間是有限的,當函式呼叫嵌套過深、區域變數過大、中斷嵌套過多等情況消耗的堆疊空間超過了預留的大小,就會發生堆疊溢出,進而覆蓋其他記憶體區域(如堆區、全域資料區甚至程式碼區)。

 

導致堆疊溢出的具體操作和場景:

1、過深的遞迴呼叫

每次遞迴呼叫都會在堆疊上壓入新的返回位址、參數、區域變數等。如果遞迴層數太多,即使每層的區域變數很小,累積起來也會耗盡堆疊空間。

2、定義過大的局部變數(尤其是陣列)

在函式內部宣告非常大的區域變數(例如大型陣列、大型結構體),比如定義一個字元緩衝區[4096];就會在函式入口時在堆疊上分配 4KB 空間。如果堆疊總大小只有 1KB 或 2KB,瞬間就會溢出。

3、過深或過長的函式巢狀:

函式 A 呼叫 B,B 呼叫 C,C 呼叫 D...。每次函式呼叫都會在堆疊上壓入返回位址、暫存器上下文(由編譯器決定)、參數(有時透過暫存器傳遞,但當參數複雜或數量很多時也會使用堆疊),以及它自己的區域變數。呼叫鏈越長,累積的堆疊消耗就越大。

4、中斷嵌套過深或中斷處理函數消耗過大

允許高優先級中斷打斷低優先級中斷(中斷嵌套),在中斷服務程序中執行耗時操作、定義大型區域變數、呼叫複雜的函數(特別是呼叫鏈較長或有大型區域變數的函數)。

5、傳遞大型結構體或陣列作為函式參數

在函式參數中使用非常大的區域變數(例如大陣列、大結構體),堆疊會需要為這個區域變數分配非常大的空間,這很容易超出堆疊的總大小。

★博文內容均由個人提供,與平台無關,如有違法或侵權,請與網站管理員聯繫。

★博文作者未開放評論功能

參考來源

: