RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

堆棧和內存的基本知識

MATLAB ? 來源:MATLAB ? 2024-08-29 14:10 ? 次閱讀

本文主要聊聊關于堆棧的內容。包括堆棧和內存的基本知識。常見和堆棧相關的 bug,如棧溢出,內存泄漏,堆內存分配失敗等。后面介紹軟件中堆棧統(tǒng)計的重要性,以及如何使用工具工具軟件中堆棧使用的范圍,并給出在軟件開發(fā)中,如何降低堆棧問題,優(yōu)化堆棧的一些實踐。

隨著代碼行數(shù)從幾千增長到百萬甚至更多,嵌入式軟件變得日益復雜,但總體目標依然是實現(xiàn)軟件的需求,達到穩(wěn)健、正確且快速執(zhí)行??焖賵?zhí)行需要以最優(yōu)方式管理可用的 CPU 和內存資源,這對內存空間(尤其是 RAM)有限的嵌入式系統(tǒng)來說是一項挑戰(zhàn)。

為此,必須通過執(zhí)行堆棧和堆分析對 RAM 的使用情況進行詳細了解。開發(fā)人員手動估計堆棧和堆負載是一項艱巨的任務,哪怕對于小程序來說也是這樣。如果估計不正確,則可能會導致堆棧溢出和一些未定義的行為。因此,常見的編碼規(guī)范要求強制執(zhí)行內存分配使用最佳實踐來避免不必要的開銷。但是,堆棧仍是 RAM 的必要組成部分,需要得到優(yōu)化利用。

堆棧和內存

先簡單聊聊內存,內存是計算機中重要的單元,而堆棧是內存中最重要的應用組成。

C 語言的內存分配

嵌入式系統(tǒng)中,內存通常被分為幾個主要區(qū)域,每個區(qū)域存儲不同類型的數(shù)據(jù),這些區(qū)域在使用方式、性能以及目的上各不相同。

下面主要說說堆區(qū)和棧區(qū)

1. 堆區(qū)

堆區(qū)用于動態(tài)內存分配,程序運行時可以從堆區(qū)動態(tài)地分配和釋放內存。其管理通常由程序的內存管理子系統(tǒng)(如 C 語言的 malloc 和 free 函數(shù))負責。堆的大小和使用效率直接影響程序的性能和穩(wěn)定性。而內存管理子系統(tǒng)通常都是程序員主動調用申請和釋放的。堆區(qū)位于 RAM 中,因此其內容在斷電后會丟失。

2. 棧區(qū)

棧區(qū)主要用于存儲局部變量、函數(shù)參數(shù)和返回地址等。棧具有后進先出(LIFO)的特性,每當調用新的函數(shù)時,函數(shù)的局部變量和返回地址就會被壓入棧中,函數(shù)返回時這些數(shù)據(jù)又會被彈出。棧區(qū)的系統(tǒng)自動管理的。棧區(qū)雖然高效但空間有限,這也導致棧溢出成為嵌入式系統(tǒng)中常見的問題之一。

堆區(qū)(Heap Memory)和棧區(qū)(Stack Memory)有以下幾個主要的區(qū)別:

1. 管理方式:

棧是自動管理的,由編譯器控制。當函數(shù)調用時,棧幀(Stack Frame)被自動創(chuàng)建和銷毀。

堆是手動管理的,需要程序員使用特定的函數(shù)(如malloc、free在C中)來分配和釋放內存。

2. 分配和釋放速度:

棧的分配和釋放速度通常較快,因為它們是連續(xù)分配的,并且不需要復雜的內存管理。

堆的分配和釋放速度較慢。差距的時間主要用在操作系統(tǒng)查找空閑內存塊,并可能涉及將內存塊從非連續(xù)的區(qū)域移動到連續(xù)的區(qū)域。

3. 內存碎片:

棧內存是連續(xù)分配的,通常不會產生內存碎片。

堆內存是動態(tài)分配的,每次大小不同,這導致堆上分配內存不連續(xù),從而出現(xiàn)內存的碎片化。這需要定期進行內存整理(Memory Compaction)來優(yōu)化性能。

堆棧統(tǒng)計為什么如此重要?

從上面關于堆棧的知識,我們知道堆棧的大小是有限的,堆棧對于程序運行極為重要。

當可用堆棧小于代碼需求時,就會發(fā)生堆棧溢出。然而,當為系統(tǒng)配置的堆棧大于需求時,內存又會被浪費。開發(fā)人員必須持續(xù)一致地估計安全關鍵型應用中最差情形下的堆棧使用量,以防止軟件運行時發(fā)生 RAM 不足的情況。

在設計嵌入式系統(tǒng)時,合理規(guī)劃和管理內存區(qū)域對于確保系統(tǒng)的性能、穩(wěn)定性和可靠性十分重要。開發(fā)者需要根據(jù)應用的具體需求和硬件資源,做出恰當?shù)膬却媸褂脹Q策。合理分配堆棧大小,通過堆棧統(tǒng)計來優(yōu)化堆棧使用,對于確保系統(tǒng)的可靠性和安全性至關重要。

內存管理不當,會導致內存泄露(堆泄露)。而內存泄漏可能會堆棧的不足,進而出現(xiàn)堆棧溢出,這些是編程中常見的錯誤之一,而且極其嚴重。對于常規(guī)的桌面級應用,這些錯誤發(fā)生會導致程序卡頓或重啟崩潰。而對于涉及生命安全和重大財產安全的關鍵應用系統(tǒng)和軟件,堆棧不足可能導致數(shù)據(jù)損壞,系統(tǒng)不穩(wěn)定和崩潰,進而導致人員傷亡和財產損失。

典型的內存問題

Memory Leak (內存泄漏)

內存泄漏更準確的說法是,堆內存泄漏 (heap leak),是程序員在分配一段內存后,分配的內存未被釋放且無法再次訪問時發(fā)生。

#include 


void leakMemory() {
    int *leak = (int*)malloc(sizeof(int) * 100);
    // 漏掉了釋放操作
}


int main() {
    leakMemory();
    return 0;
}

例子中,指針 leak 作為局部變量,在退出 leakMemory 函數(shù)后,沒有釋放且找不到地址無法再次訪問。

Stack Leak (棧泄漏)

當程序中的局部變量大量消耗棧資源,而又沒有退出該函數(shù),導致 stack 溢出,大量的溢出可能會導致棧的不足,從而發(fā)生 overflow 的情況。這種一般發(fā)生在遞歸函數(shù)或者函數(shù)中有大循環(huán),其有定義局部變量,比如下面的代碼

void stackLeak(int n) {

char buffer[1024];

printf("Leaking stack memory %d,%p\n", n, (void *)buffer);

if(n>1)

stackLeak(n - 1);

}

int main() {

stackLeak(500);

return 0;

}


在 32G 內存的筆記本上,運行到 373 次就棧溢出了。


wKgaombQES2AFnb1AAEisWjn2dk976.png ?
wKgZombQETGAVjLYAAGCkdWqPOQ296.png

Buffer overflow(緩沖區(qū)溢出)

Buffer overflow(緩沖區(qū)溢出)是一種常見的安全漏洞,通常發(fā)生在當程序試圖向一個固定長度的緩沖區(qū)寫入過多數(shù)據(jù)時,一般發(fā)生在字符操作的時候。盡管緩沖區(qū)溢出通常與堆棧溢出有所區(qū)別——前者涉及對固定大小緩沖區(qū)的寫操作超出其邊界,后者是函數(shù)調用和局部變量使用過多堆??臻g——但在實踐中,緩沖區(qū)溢出經常導致堆棧上的數(shù)據(jù)被覆蓋,因此可以視為一種堆棧不足引發(fā)的問題。

以下是一個使用C語言的示例,展示了一個簡單的緩沖區(qū)溢出漏洞:

#include 
#include 


void vulnerableFunction(char *str) {
    char buffer[10];
    strcpy(buffer, str); // 不安全的拷貝,拷貝應該指定大小
    printf("Buffer content: %s
", buffer);
}


int main() {
    char largeData[] = "這是一個超長的字符串,遠遠超過了buffer的容量";
    vulnerableFunction(largeData);
    return 0;
}

在這個例子中,vulnerableFunction函數(shù)定義了一個長度為 10 的字符數(shù)組buffer作為緩沖區(qū)。然后,它使用strcpy函數(shù)將傳入的字符串str拷貝到buffer中。如果str的長度超過了buffer的容量(在這個例子中是 10 個字符),就會發(fā)生緩沖區(qū)溢出。strcpy不會檢查目標緩沖區(qū)的大小,所以它會繼續(xù)寫入數(shù)據(jù),直到遇到源字符串的結束符\0。

這種溢出可能會覆蓋堆棧上的其他重要數(shù)據(jù),比如其他局部變量、函數(shù)返回地址等,導致程序行為異常,甚至允許黑客執(zhí)行任意代碼。

為了避免這種安全漏洞,應該使用更安全的函數(shù),如strncpy,它允許指定目標緩沖區(qū)的最大長度,從而避免溢出:

strncpy(buffer, str, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '?'; // 確保字符串以 null 結束

這樣就可以顯著減少因緩沖區(qū)溢出導致的安全風險。

Stack Frame Corruption (棧幀破壞)

棧幀中是函數(shù)的局部變量和函數(shù)調用時候的相關開銷。當我們對局部變量進行錯誤的操作時候,可能會破壞棧幀,導致函數(shù)的返回地址或其他重要數(shù)據(jù)被覆蓋。舉例如下。 #include void corruptStackFrame() { int arr[1] = {0}; int b = 10; int c = 20; arr[1] = 0; // 故意寫入數(shù)組界限之外,可能覆蓋返回地址 arr[2] = 0; // 故意寫入數(shù)組界限之外,可能覆蓋返回地址 printf("b=%d c=%d ", b, c); } int main() { corruptStackFrame(); return 0; }

上面的例子中,有明顯的數(shù)組越界的問題。同時,由于該數(shù)組是局部變量,對數(shù)組外的數(shù)進行操作,可能會導致周邊的棧幀給改寫,從而導致系統(tǒng)崩潰。

wKgaombQEY2AcsZlAAIKCuS-CwU962.jpg

當然棧幀是否被改寫可能涉及很多系統(tǒng)的很多方面。這里不詳細討論。

Memory Allocation Failed

當請求的內存無法被分配時發(fā)生。當請求大量內存時,可能會因為內存不足或者沒有足夠的連續(xù)內存導致分配內存失敗。

#include 


int main() {
    //分配大量內存
    int *bigArray = (int*)malloc(sizeof(int) * 1000000000);
    if (bigArray == NULL) {
        printf("Memory allocation failed
") //內存分配失敗
    }
    free(bigArray);
    return 0;
}

其他內存管理方面的問題還包括重復分配內存,野指針問題等,都會直接或間接的導致可用堆棧的減少。

歷史上,許多著名的軟件漏洞,如 Heartbleed、Spectre 等,都與堆棧管理不當有關。通過對堆棧進行統(tǒng)計,我們可以提前發(fā)現(xiàn)潛在的安全隱患,避免類似問題的發(fā)生。

Heartbleed 漏洞是由于 OpenSSL 庫中的堆棧管理錯誤導致的。該漏洞允許攻擊者讀取內存中的敏感信息,甚至可以修改內存內容。通過對堆棧進行統(tǒng)計和分析,可以發(fā)現(xiàn) OpenSSL 庫中的堆棧使用不當,從而避免 Heartbleed 漏洞的產生。

堆棧統(tǒng)計促進軟件開發(fā)和性能優(yōu)化

堆棧統(tǒng)計不僅可以幫助開發(fā)者確定程序在運行時堆棧的使用情況,還可以指導開發(fā)者進行性能優(yōu)化。通過準確的堆棧使用數(shù)據(jù),開發(fā)者可以合理分配堆棧大小,既避免了堆棧溢出的風險,也確保了系統(tǒng)資源的高效利用。

性能瓶頸定位:堆棧統(tǒng)計可以幫助開發(fā)者快速定位應用程序中的性能瓶頸。通過分析哪些函數(shù)調用最頻繁或哪些調用耗時最長,開發(fā)者可以集中優(yōu)化這些熱點區(qū)域,從而提高整體應用性能。

資源使用分析:它可以幫助開發(fā)者理解應用程序如何使用系統(tǒng)資源,例如CPU和內存。這對于識別和修復內存泄漏、過度的CPU使用等問題非常重要。

代碼質量改進:通過堆棧統(tǒng)計,開發(fā)者可以識別代碼中的不良實踐或設計模式,如不必要的遞歸、過度復雜的函數(shù)調用等,進而重構代碼以提高其可讀性和可維護性。

優(yōu)化決策依據(jù):堆棧統(tǒng)計提供了量化數(shù)據(jù),幫助開發(fā)團隊做出基于數(shù)據(jù)的決策。這些數(shù)據(jù)可以用來確定優(yōu)化的優(yōu)先級,決定哪些優(yōu)化措施可以帶來最大的性能提升。

性能回歸檢測:在軟件開發(fā)周期中,新的代碼提交可能會引入性能回歸。定期進行堆棧統(tǒng)計可以幫助及時發(fā)現(xiàn)性能下降,確保軟件性能持續(xù)穩(wěn)定。

用戶體驗提升:應用程序的響應速度直接影響用戶體驗。通過優(yōu)化那些影響性能的關鍵部分,可以顯著提升應用的響應速度和流暢度,從而提高用戶滿意度。

成本效益分析:對于需要大量計算資源的應用程序,堆棧統(tǒng)計可以幫助識別和優(yōu)化資源密集型操作,從而減少對硬件資源的需求,降低運營成本。

總之,堆棧統(tǒng)計是理解和優(yōu)化軟件性能的強大工具。通過定期進行堆棧統(tǒng)計和分析,開發(fā)團隊可以確保他們的應用程序運行高效,提供優(yōu)秀的用戶體驗,并以最佳的資源使用效率運作。

人工 VS 工具統(tǒng)計

在有相關統(tǒng)計工具之前,嵌入式系統(tǒng)的堆棧都是人工統(tǒng)計。此舉雖然可行,但是實際操作中存在許多問題和不足。

耗時耗力燒腦。人工統(tǒng)計需要徹底了解函數(shù)調用的深度、所有局部變量的細節(jié),以及執(zhí)行過程中隨時發(fā)生的中斷幀的大小對于復雜的嵌入式系統(tǒng)而言幾乎是不可行的。

漫長而又容易出錯的過程

沒法保證準確性,特別是在面對大量并發(fā)執(zhí)行的任務和復雜的函數(shù)調用關系時,更是這樣。

實時更新,則更是無法做到,每一次代碼的變更,可能都需要做大量的重新統(tǒng)計。

工具分析能夠詳細分析出函數(shù)調用深度、局部變量和返回參數(shù)的堆棧估計、嵌套中斷以及執(zhí)行期間發(fā)生的中斷幀的大小,解決人工統(tǒng)計的上述問題之外,還有另外兩個優(yōu)勢。

動態(tài)分析:一些高級的堆棧統(tǒng)計工具支持運行時分析,能夠實時監(jiān)控堆棧的使用情況,及時發(fā)現(xiàn)潛在的堆棧溢出風險。這種一般是芯片廠家或者合作廠家提供的調試工具中。

能夠對目標機進行測試和測量。對戰(zhàn)統(tǒng)計最好在真實硬件上獲得實際的堆棧使用量數(shù)據(jù)。許多開發(fā)環(huán)境都具有硬件模擬功能,并提供實時堆棧分析功能。在實際硬件上執(zhí)行堆棧分析,并創(chuàng)建溢出場景來測試故障安全例程

可視化效果好。許多工具提供圖形化界面,直觀展示堆棧的使用情況,函數(shù)調用情況以及隨著函數(shù)調用,堆棧使用的變化,幫助開發(fā)者更容易理解和分析數(shù)據(jù)。

市面上常見的堆棧統(tǒng)計軟件

堆棧統(tǒng)計工具有以下幾類

一類是由編譯和 調試工具,有芯片廠商提供的,也有第三方的。比如 ARM Keil MDK,IAR Embedded Workbench 等等

第二類是第三方的調試工具,GNU 項目,如 GNU Debugger,Lauterbach Trace32

第三類是開源堆棧工具,如 Valgrind,FreeRTOS 中的 vTaskList

最后一類是靜態(tài)分析工具。這一類以 polyspace code prover 為代表。

以上就是關于內存、堆棧方面的一些介紹,以及常見的一些內存泄露相關的例子,這些例子會直接或間接到導致內存泄露,進而導致可用堆棧的減少,軟件長時間運行會導致性能下降,甚至崩潰等問題。處理好這些問題是解決堆棧問題的前提。

另外并介紹了堆棧統(tǒng)計的收益。

下一篇我們看看,如何使用形式化工具進行堆棧統(tǒng)計。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5082

    文章

    19104

    瀏覽量

    304791
  • 計算機
    +關注

    關注

    19

    文章

    7488

    瀏覽量

    87848
  • 內存
    +關注

    關注

    8

    文章

    3019

    瀏覽量

    74002
  • C語言
    +關注

    關注

    180

    文章

    7604

    瀏覽量

    136683
  • 堆棧
    +關注

    關注

    0

    文章

    182

    瀏覽量

    19753

原文標題:堆棧統(tǒng)計知多少(一)關于內存,堆棧和常見的 BUG

文章出處:【微信號:MATLAB,微信公眾號:MATLAB】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    堆棧內存和堆內存之間的區(qū)別

    編寫有效的代碼需要了解堆棧和堆內存,這使其成為學習編程的重要組成部分。不僅如此,新程序員或職場老手都應該完全熟悉堆棧內存和堆內存之間的區(qū)別,
    發(fā)表于 08-07 12:23 ?721次閱讀
    <b class='flag-5'>堆棧</b><b class='flag-5'>內存</b>和堆<b class='flag-5'>內存</b>之間的區(qū)別

    電工基本知識

    電工基本知識
    發(fā)表于 09-21 16:34 ?0次下載
    電工<b class='flag-5'>基本知識</b>

    LDO基本知識

    LDO基本知識
    發(fā)表于 02-09 10:26 ?103次下載
    LDO<b class='flag-5'>基本知識</b>

    MIMO 的基本知識介紹

    MIMO 的基本知識介紹 很好的初學者入門書籍
    發(fā)表于 06-25 14:47 ?25次下載

    功率MOSFET的基本知識

    功率MOSFET的基本知識
    發(fā)表于 04-16 23:34 ?2355次閱讀
    功率MOSFET的<b class='flag-5'>基本知識</b>

    繼電器基本知識

    繼電器基本知識
    發(fā)表于 06-30 19:28 ?1838次閱讀

    網(wǎng)絡基本知識教程

    網(wǎng)絡基本知識教程
    發(fā)表于 01-13 12:55 ?1576次閱讀

    HFC網(wǎng)絡基本知識

    HFC網(wǎng)絡的基本知識講解
    發(fā)表于 11-08 17:30 ?59次下載
    HFC網(wǎng)絡<b class='flag-5'>基本知識</b>

    LED基本知識

    介紹LED的基本知識以及LED的分類。
    發(fā)表于 05-30 14:58 ?0次下載

    安全用電基本知識

    安全用電基本知識安全用電基本知識安全用電基本知識
    發(fā)表于 01-14 15:54 ?0次下載

    UPS電源的基本知識

    電子專業(yè)單片機相關知識學習教材資料——UPS電源的基本知識
    發(fā)表于 09-13 17:46 ?0次下載

    光纖基本知識

    光纖基本知識
    發(fā)表于 12-15 22:26 ?0次下載

    線程的基本知識

    【RT-Thread】線程的基本知識
    的頭像 發(fā)表于 02-04 15:42 ?3474次閱讀
    線程的<b class='flag-5'>基本知識</b>

    CPLD/FPGA的基本知識

    CPLD/FPGA的基本知識講解。
    發(fā)表于 03-30 09:55 ?31次下載
    CPLD/FPGA的<b class='flag-5'>基本知識</b>

    電氣基本知識科普

    電氣基本知識科普
    的頭像 發(fā)表于 09-09 10:23 ?6115次閱讀
    電氣<b class='flag-5'>基本知識</b>科普
    RM新时代网站-首页