田宇,馬朝陽,趙昶宇
?
嵌入式系統動態內存管理及故障檢測
田宇1,馬朝陽2,趙昶宇3
(1.海軍駐天津八三五七所軍事代表室,天津 300308;2.海軍舟山地區裝備修理監修室,浙江 舟山 316000; 3.天津津航計算技術研究所,天津 300308)
針對嵌入式軟件內存管理具有快速性、可靠性和高效性的特點,建立了動態內存管理模型,分析了嵌入式系統內存池管理策略和內存分配算法,詳細闡述了內存泄露檢查和內存重復釋放檢查的具體算法,并對嵌入式軟件的內存操作提出了建議。
VxWorks;內存池;內存泄露;內存釋放
快速性、可靠性和高效性是嵌入式開發對內存管理的基本要求,對實時性要求很高的VxWorks操作系統,內存管理機制是研究的重要領域。VxWorks系統中最基本的內存分配方案有靜態分配和動態分配。靜態分配是指在編譯或鏈接時將程序所需要的內存空間分配好,采用這種分配方案的程序段,其大小一般在編譯時就能確定。而動態分配是指系統運行時根據需要,動態地分配內存空間。這兩種分配策略各有利弊。靜態分配內存、系統的可靠性高,但失去了靈活性,而且浪費也較多;動態分配具有靈活、利用率高等優點,但會導致響應時間的不確定及容易產生碎片等問題。本文重點分析VxWorks動態內存管理機制,并對內存泄露和內存重復釋放等內存故障問題的檢測和診斷給出了解決方案。
動態內存是一個自由存儲區,編程人員根據需要進行動態內存的分配、訪問、回收。對動態內存的操作與系統當前的動態內存狀態有關。在使用動態內存之前,必須向自由存儲區申請內存。由于自由存儲區的容量是有限的,因此在動態內存分配之后和動態內存使用之前還要進行分配結果的檢測,以保證后續操作的正確性;在程序結束前,要進行動態內存的釋放操作,防止內存發生泄露。圖1描述了一個動態內存管理過程,圖中的節點表示動態內存管理過程中的幾個階段,圖中的連線表示各階段之間的遷移。
圖1刻畫了內存管理的幾個階段:①Start。程序開始。②Memory allocate。對指針進行動態內存分配,若沒有足夠的動態內存或者分配過程發生錯誤,則將該指針置為NULL。③Check memory。用來檢查分配的結果,以保證后續動態內存操作的正確性。④Access memory。對動態內存中的內容進行讀、寫、修改等操作。⑤Free memory。動態內存操作的結束,進行動態內存釋放。⑥End。程序結束。
圖1中實心箭頭表示程序正確執行時的動態內存管理路徑,而虛線箭頭表示的路徑指出動態內存使用后未被釋放,即發生了動態內存泄露。

圖1 動態內存管理過程模型
VxWorks內存管理函數庫分為完全內存管理函數庫(庫名稱為memLib)和核心內存管理函數庫(庫名稱為memPartLib)。其中核心內存管理庫為內存分區的分配、管理提供了核心函數,以mem Part開頭的包含了創建、管理、分配、釋放內存分區的函數,其余的提供了與ANSI標準相兼容的對系統內存分區的操作接口。當應用程序從系統分區中再創建其他分區時,就得調用函數malloc進行動態分配。一般系統中只有1個內存分區,即系統分區,所有任務所需要的內存直接調用malloc從系統分區中進行分配,使用完后調用free進行內存釋放,通過free釋放的內存將被聚合形成更大的空閑塊,這就是VxWorks的動態內存分配機制。
應用程序申請使用系統內存區時,調用函數malloc/free 雖然靈活方便,但它有時間不確定、會產生過多的碎片、不能用于中斷服務程序、降低系統性能等缺點,所以不宜頻繁使用。一般在系統設計時,采用動態分配與靜態分配相結合的方法來管理系統內存池,來避免單一內存分配所帶來的弊端。內存池的引入可以極大加快內存分配/釋放過程,減少動態分配、釋放內存時的消耗,并且可以有效減少內存碎片,避免內存泄露。一般內存池結構可由單元尺寸、單元數量、空閑鏈表、調試參數等組成。由于利用了內存池的內存分配方案在VxWorks系統中,使得系統減少了malloc/free的調用次數,減少了碎片,同時用戶可自由添加一些用于內存分配和釋放的調試函數,更好地監視了內存的使用情況。
嵌入式實時動態內存的傳統分配方式,往往是系統的碎片隨物理內存的增加呈線性增長,導致碎片的組合操作所付出的代價很高,目前常用的解決方案是以頁表作為管理結構,頁面作為管理基礎,實現了對內存分配與回收的實時管理。
VxWorks內存管理是基于一種Flat模式,從宏觀的層次可以分成Partition(分區)、Block(塊)、Pool(池)的框架。由于系統只有一個分區,所有任務所需內存直接由malloc從其中分配。最常用算法為First-Fit(最先分配)算法,此算法思想是:空閑內存塊按地址大小遞增排列,對于要求分配的分區容量size,從頭開始比較,直至找到滿足不小于size的塊為止,并從鏈表相應塊中分配出相應size大小的指針。從空閑鏈表中查找內存塊,從高地址開始,當找到滿足第一個分配請求空閑塊時就分配所需的內存,并修改該空閑塊的大小,空閑塊的剩余部分仍然保留在空閑鏈表中。動態內存釋放時,根據塊頭中的信息判斷相鄰的內存塊是否空閑,如果將空閑塊合并,并修改長度,否則就把新釋放的內存插入到空閑鏈表中。
First-Fit算法在提高系統實時性的同時,也出現一些問題,比如2個任務占用2個連續的Block,當Block1操作越界后,Block2數據會被破壞,同時在系統分配時會產生很多碎片等,這些都會對系統運行產生影響。
內存泄露一般是指堆內存的泄漏。堆內存是指程序從堆中分配的、使用完畢后必須釋放的內存。也就是說,程序在運行過程中,如果需要動態內存來協助程序執行,則會向操作系統從堆中申請一塊內存,待使用完畢之后,將該內存空間釋放,及時歸還給操作系統。內存泄漏是指由于疏忽或者錯誤程序未能釋放已經不再使用的內存的情況。
如果運行在嵌入式平臺上的軟件程序存在內存泄露,輕則會降低系統的性能,最糟糕的情況是所有堆內存被分配,程序無法再次從堆內存中申請到程序執行所必須的內存,從而導致全部或者部分程序、設備無法正常工作,造成系統癱瘓甚至崩潰。
在VxWorks系統下,memShow可以顯示系統內存分配情況,包括已分配內存和空閑內存大小、塊數、平均大小等信息。本文提出的內存泄漏診斷算法的基本思想是:在應用程序申請堆內存的時候,將申請信息記錄在一個后臺程序管理的全局數據結構中;在應用程序釋放堆內存的時候,將后臺程序管理的全局數據結構中的記錄刪除;當程序運行到一個穩態,即程序被認為不應該有堆內存未被釋放的狀態,將后臺程序管理的這個全局數據結構中的記錄輸出到指定文件,供測試人員檢查。后臺全局數據結構中記錄的信息應該至少包括申請的堆內存的首地址、申請的堆內存的大小、申請該堆內存的代碼行號和文件名。后臺程序在一個單獨的任務中實現。
2.1.1 增加一個內存泄露檢查跟蹤結點模塊
該模塊在嵌入式軟件程序中申請堆內存的時候被調用,同時,“申請成功的堆內存首地址”“申請成功的堆內存長度”“成功申請堆內存程序的源文件名稱”和“成功申請堆內存程序代碼所在源文件中的行號”作為該模塊的輸入,該模塊將在堆內存檢查管理鏈表中添加一個結點并將輸入信息記錄。本算法中,使用帶有表頭的單向鏈表來記錄和管理程序的堆內存申請信息。每申請一次內存,增加一個結點,釋放一次內存,刪除一個結點。程序詳細流程如圖2所示。

圖2 增加一個內存泄露檢查跟蹤結點模塊流程圖
2.1.2 刪除一個內存泄露檢查跟蹤結點模塊
該模塊在嵌入式軟件程序當釋放堆內存的時候被調用,同時,“申請成功的堆內存首地址”“申請成功的堆內存長度”“成功申請堆內存程序的源文件名稱”和“成功申請堆內存程序代碼所在源文件中的行號”作為該模塊的輸入,該模塊將在堆內存檢查管理鏈表中添加一個結點并將輸入信息記錄。程序詳細流程如圖3所示。
2.1.3 內存泄露檢查跟蹤結點信息輸出模塊
該模塊在一個測試用例被執行完畢的時候被調用,也可以在使用者認為需要的時候調用。程序詳細流程如圖4所示。
重復釋放是指在程序申請到一塊堆內存之后,經過使用將這塊內存釋放,但沒有將指向這塊內存的所有指針回收,并在程序的其他部分再次將指向同一塊內存單元的指針交給內存分配器執行堆內存釋放的操作。堆內存重復釋放檢查算法是在內存泄漏檢查算法的基礎上開發與設計的。重復釋放檢查算法使用一個單向鏈表記錄程序的重復釋放信息,在測試用例執行結束之前將該信息輸出。
2.2.1 增加一個內存重復釋放檢查跟蹤結點模塊
該模塊在嵌入式軟件程序釋放堆內存的時候被調用,同時,“釋放的堆內存地址”、“釋放的堆內存的源文件名稱”和“釋放的堆內存程序代碼所在源文件中的行號”作為該模塊的輸入,該模塊將在重復釋放堆內存檢查管理鏈表中添加一個結點并將輸入信息記錄。程序詳細流程如圖5所示。

圖3 刪除一個內存泄露檢查跟蹤結點模塊流程圖

圖4 內存泄露檢查跟蹤結點信息輸出模塊流程圖

圖5 增加一個內存重復釋放檢查跟蹤結點模塊流程圖
2.2.2 內存重復釋放檢查跟蹤結點信息輸出模塊
該模塊在一個測試用例被執行完畢的時候被調用,也可以在使用者認為需要的時候調用,詳細流程圖如圖6所示。
2.2.3 避免內存泄露和內存重復釋放的建議
內存泄露和內存重復釋放是在設計與編碼期間引入的,當使用 C/C++ 進行開發時,采用一致的編程規范和養成良好的習慣是防止內存泄漏發生的第一步,也是最重要的措施。應注意以下事項:①malloc/calloc與free、new與delete、open與close、fopen與fclose 等一定要成對出現,特別是在函數帶有判斷分支語句退出時,一定記得在每個分支都要釋放該釋放的內存、關閉必要的文件句柄;②正確處理malloc、calloc、open/fopen 等函數的返回值;③在釋放結構指針時,一定要遍歷釋放(處理)必要的結構中每個成員指針;④對于嵌入式系統而言,系統通常是一個確定的系統,對于系統中用到的可以確定大小內存,建議直接用數組或結構,不要動態申請。

圖6 內存重復釋放檢查跟蹤結點信息輸出模塊流程圖
本文分析了VxWorks系統的動態內存管理機制,建立了動態內存管理模型,闡述了內存池管理策略和內存分配算法,在此基礎上,對編程中容易出現的內存泄露和內存重復釋放的問題給出了具體的故障檢測算法,并對內存操作提出了建議。本文的內容有助于提升VxWorks系統軟件的開發人員和測試人員的技能水平。
[1]顧勝元,楊丹,黃海倫.嵌入式實時動態內存管理機制研究與應用[J].重慶工學院學報(自然科學),2009(1):117-121.
[2]殷戰寧,劉琳.VxWorks的內存配置和管理[J].艦船電子對抗,2012,35(3):104- 109.
[3]何煦嵐,何曉嵐.基于多鏈表結構的嵌入式系統內存管理[J].計算機應用與軟件,2008,25(4):58- 60.
2095-6835(2018)21-0006-03
TP316.2
A
10.15913/j.cnki.kjycx.2018.21.006
田宇(1984—),男,工學碩士,工程師,主要從事裝備質量監督與檢驗驗收方面的工作與研究。馬朝陽(1976—),男,大學本科,工程師,主要從事艦艇裝備保障方面的工作與研究。趙昶宇(1982—),男,陜西漢中人,工學碩士,高級工程師,主要從事嵌入式系統軟件測試方面的研究。
〔編輯:嚴麗琴〕