肖蕾 劉克江
摘 要:在嵌入式系統領域,如何合理地分配和管理系統內存RAM資源是程序員必須面對的問題,能否高效、可靠、實時地管理動態內存分區決定了整個系統的穩定性和可靠性。本文以μC/OS與FREERTOS兩種操作系統為例,在深入研究其動態內存管理機制的基礎上,對其優缺點和適用場合進行了分析比較,便于軟件開發人員在實際應用中根據產品不同需求進行針對性的選擇。
關鍵詞:μC/OS FREERTOS;嵌入式系統;動態內存;管理機制
中圖分類號:TP316.2 文獻標識碼:A
Abstract:In the embedded system field,every programmer has to face the problem about how to reasonably allocate and manage the RAM resource.Stability and reliability of a whole system are determined by the programmer's capability to carry out efficient,reliable and real-time management of dynamic memory partition.Based on in-depth study of μC/OS and FREERTOS dynamic memory management mechanism,the paper comparatively analyzes the advantages,disadvantages and the application occasions,which facilitates developers to make targeted selection of operating systems based on different product requirements in practical applications.
Keywords:μC/OS;FREERTOS;embedded systems;dynamic memory;management mechanism
1 引言(Introduction)
在嵌入式系統領域中,內存RAM一直是一種稀缺資源。如何合理地分配和管理系統的內存資源是嵌入式軟件程序員必須面對的問題,特別是在產品必須使用到動態內存分配時,能否高效、可靠、實時地管理動態內存分區決定了整個系統的穩定性和可靠性[1,2]。
針對這種情況,程序員常用的解決辦法主要包括下面三種:在系統啟動時就按最壞考慮分配足夠大的數組、根據實際需求自行編寫內存管理程序或直接使用編譯器提供的malloc和free函數、基于嵌入式操作系統內存管理機制來處理。如果程序員能夠充分掌握整個系統所有可能出現的情況,根據最壞需求在系統啟動時就給每一項作業分配一塊足夠大的數組是最簡單和直接的方法,但勢必會造成內存浪費,且如果作業需要數組類型是多種的情況很可能會陷入內存空間不足的困境[3]。根據產品軟件設計的需求自行編寫簡單短小的內存管理程序,對于有經驗程序員來說不成問題,但是這種方法也存在著不同平臺通用性較差、管理程序穩定性和可靠性因人而異,另外,malloc和free函數并不是所有平臺都可以使用且代碼不可見。移植現成的嵌入式操作系統,基于操作系統下的內存管理機制來處理系統的動態內存問題是比較方便而且可靠的解決辦法,但程序員必須深入了解所使用操作系統動態內存管理機制的特點和區別,并能夠針對不同的處理器資源對操作系統進行一定程度裁剪。
上述三種方法中程序員大多比較傾向于移植現有成熟的嵌入式操作系統來處理,本文以μC/OS與FREERTOS兩種操作系統為例,在深入研究其動態內存管理機制的基礎上,對其優缺點和適用場合進行了分析比較,便于軟件開發人員在實際應用中根據產品不同需求進行針對性的選擇。
2 內存管理算法(Memory management algorithm)
2.1 μC/OS動態內存管理
在μC/OS操作系統中,使用動態分配內存時必須先調用OSMemCreate函數建立并初始化一個內存區,該內存區會被分割成n塊固定大小的內存塊。OSMemCreate函數傳遞參數指定內存區起始地址、每塊內存塊大小以及內存塊數量。初始化后的結構在每一塊空閑塊開頭存放著指向下一塊空閑塊指針。初始化后μC/OS為每一個動態內存區定義一個“內存控制塊”來記錄和跟蹤該區的使用情況,包含內存分區指針、空閑塊鏈表指針、每塊內存大小、內存塊數目和空閑內存塊數目,其結構為:
typedef struct
﹛
void *OSMemAddr;
void *OSMemFreeList;
INT32U OSMemBlkSize;
INT32U OSMemNBlks;
INT32U OSMemNFree;
﹜OS_MEM;
當用戶程序調用函數OSMemGet申請一塊動態內存時,系統便通過“內存控制塊”將空閑塊鏈表指向的第一塊空閑內存塊分配給程序,同時將空閑塊鏈表指針指向下一個空閑塊并更新空閑塊數目。在程序需要釋放內存時調用OSMemPut函數,系統根據“內存控制塊”把回收的內存塊插入到空閑塊鏈表表頭,并更新空閑塊數目。由此可見,μC/OS每次分配和回收動態內存的時間是確定的,每一次分配和回收的內存大小已知,且沒有內存碎片的存在。
μC/OS為了保證內存管理時間確定性和解決內存碎片問題,在用戶程序初始化一塊動態內存區時便指定了該內存區每一塊內存塊長度,申請動態內存時只能得到初始化時指定的大小,OSMemGet這個函數并不需要用戶程序指定需要申請的內存長度,內存塊長度由圖2內存控制塊中的OSMemBlkSize決定。如果出現多個需要動態使用內存的任務,且每個任務所需的內存塊長度都不一樣,程序員可以多次調用OSMemCreate函數創建包含不同大小內存塊的動態內存區,最大創建的內存區數目可通過OS_MAX_MEM_PART設定,然后程序需要多大的內存塊就到對應內存控制塊中申請即可;另一種做法就是調用OSMemCreate時根據最大內存塊大小去初始化一塊動態內存區,所有程序都在該內存區中申請和釋放動態內存,在調用OSMemGet得到一塊較大的內存塊時強制轉換成具體程序所需的數據格式即可。
通過分析可知μC/OS處理動態內存的方法具有如下優點:
(1)μC/OS對動態內存分區的管理機制在操作上時間是可確定的。
(2)不會產生所謂動態內存碎片,可以最大限度保證系統的穩定性和可靠性。
(3)每一動態內存塊大小固定,每個空閑內存塊頂部只需存儲下一個空閑塊指針,減小了系統額外開支。
但是,該算法的缺點也是顯而易見的:
(1)不能靈活充分利用整個內存空間。無論是創建多個內存區還是根據最大內存塊創建一個內存區,都會造成嚴重的內存空間浪費。
(2)缺乏靈活性。用戶程序之所以使用動態內存分配目的便是為了提高靈活性,但是μC/OS在初始化動態內存區時便將其劃分為固定大小的連續存取區,這樣在某些時候不能確定某個內存塊大小時便無法通過該算法解決問題。
(3)不檢查回收內存塊的合法性。μC/OS的內存回收函數OSMemPut在回收內存時并不檢查所回收的內存塊是否是本動態內存區的空間,用戶程序調用OSMemPut函數時傳遞任何參數,只要內存控制塊中OSMemNFree小于OSMemNBlks便將該參數指向的空間作為空閑塊鏈表節點插入到空閑塊鏈表中,這種情況導致的后果是將是不可預估的。
2.2 FreeRTOS動態內存管理
FreeRTOS是一個微型嵌入式操作系統內核,具有源碼公開、免費、可裁剪、調度策略靈活和簡單易用等特點,被很多嵌入式開發人員所選用[4,5]。對于內存管理,FreeRTOS根據使用者實際需求提供三種策略,每種策略對應獨立的源文件,需要將對應的文件移植到工程中[6-8]。
策略一是三個方案中最簡單的,系統根據onfigTOTAL_HEAP_SIZE設定的大小劃分一塊內存作為動態內存區,同時定義變量xNextFreeByte標志空閑區域的位置,初始值為0。當用戶程序申請動態內存時便返回當前xNextFreeByte代表的內存地址,并將xNextFreeByte加上所申請內存塊長度,且內存一旦分配便不允許釋放,策略一中沒有提供內存回收方法。
策略二中建立空閑分區鏈表采用首次適應算法分配動態內存,允許分配后的動態內存調用釋放函數進行回收,然而,它不具備將鄰近空閑塊合并成一個大空閑塊的功能。FreeRTOS為動態內存分區中每一塊空閑塊建立一個“空閑分區節點”,并將該節點存放于空閑塊頂部。該節點數據結構包含指向下一節點指針和本空閑分區大小。同時定義一個開始節點和終止節點作為空閑分區鏈表的表頭和結尾,當用戶程序調用函數pvPortMalloc申請動態內存時,便從空閑分區鏈表表頭節點開始查找合適大小的內存塊(即該空閑內存分區大于或等于所申請內存),找到則返回該空閑分區存儲地址并修改該塊“空閑分區節點”內容,判斷該塊剩余空間是否可以創建“空閑分區節點”,可以則將該塊剩余空間劃分為新的空閑塊并建立新的“空閑分區節點”,最后更新空閑分區鏈表,FreeRTOS空閑分區節點數據結構如下所示。
typedef struct A_BLOCK_LINK
﹛
struct A_BLOCK_LINK *pxNextFreeBlock;
size_t xBlockSize;
﹜xBlockLink;
通過上述分析可以得出FreeRTOS策略二具有下列優點:
(1)根據用戶程序申請的每一塊動態內存大小建立一個空閑分區節點記錄該動態內存的信息,真正意義上實現了動態分配。
(2)用戶不必在系統啟動時初始化動態內存區,對外接口函數只有pvPortMalloc和vPortFree,很大程度上降低了使用動態內存的難度。
(3)系統采用首次適應算法減小了分配和回收動態內存時的查找時間。
同樣,該策略也存在著如下缺點:
(1)每次分配和回收動態內存的時間不固定,即存在著時間不確定性。
(2)如果用戶程序需要頻繁分配和回收大小不同的動態內存塊時,隨著系統運行,可能會出現空閑分區鏈表越來越大,整個動態內存分區會被分割成很多個細小的內存碎片,且每一個內存碎片都附帶一個空閑分區節點,造成內存空間的大量浪費。
(3)一旦出現(2)所述的現象,整個系統的穩定性將會降低,甚至會引發系統的崩潰,盡管這種情況并不是開發人員編程邏輯算法錯誤造成的,但是FreeRTOS并沒提供方法或者試圖去阻止這種情況發生。
(4)FreeRTOS在回收內存時同樣沒有檢查用戶程序所釋放內存塊的合法性,這是因為FreeRTOS采用從小到大排列空閑分區塊,這樣便找不到一種很好的算法去判別所釋放內存塊是否屬于動態內存區和該動態內存塊結構有沒有遭到破壞。
策略三只是對標準malloc()和free()函數線程安全方面的進行包裝。具體做法是當用戶程序調用pvPortMalloc申請動態內存的時候,暫停系統任務調度,將pvPortMalloc的參數傳給malloc函數,最后開啟任務調度,并將malloc返回值傳遞給用戶程序,在釋放內存時調用vPortFree所做步驟也是一樣的。
3 結論(Conclusion)
通過對μC/OS與FREERTOS兩個常用小型嵌入式操作系統內存管理機制的分析與比較可知:μC/OS動態內存管理機制將系統實時性和可靠性放在第一位,甚至犧牲了內存空間和動態內存分配的靈活性以換取絕對確定的分配和回收時間,如果產品實時性和可靠性要求很高,有足夠內存空間可供使用,對于動態內存分配情況在設計初始便能確定的情況下,μC/OS動態內存管理機制便是首選了;反之如果用戶程序對動態內存分配和回收情況無法確定或者所申請大小是隨機的,由于控制成本導致沒有足夠大內存空間可供使用,且要求動態內存管理機制具備很高的靈活性,這樣便只能選擇實時性和可靠性相對不足的FreeRTOS系統。
本文以μC/OS與FREERTOS兩種操作系統為例,在深入研究其動態內存管理機制的基礎上,對其優缺點和適用場合進行了分析比較,說明了產品軟件設計階段動態內存分配機制的選擇在一定程度上左右著整個系統的成本和性能,在低成本嵌入式產品中使用到動態內存,必須根據產品的實際情況選擇合適的動態內存管理算法,便于軟件開發人員在實際應用中根據產品不同需求進行針對性的選擇。
參考文獻(References)
[1] Lu Xiao-shuang,Shuai Jian-mei,Wu Qing-xiang.Novel memory manager for object-oriented programs[J].Computer Engineering,2012,38(9):21-23.
[2] Zhang Fei.The dynamic memory management research of real-time embedded operating system[D].Hefei:University of Science and Technology of China,2011.
[3] Gao Chao,Han Rui,Ni Hong.Memory management solution in embedded linxux systems[J].Journal of Chinese Computer Systems,2011,32(4):614-618.
[4] 黃鵬程.嵌入式實時操作系統FreeRTOS在ARM7上移植的實現[J].中國電子商情:通信市場,2009(3):59-64.
[5] 張龍彪,張果.嵌入式操作系統FreeRTOS的原理與移植實現[J].信息技術,2012(11):31-34.
[6] Richard Barry.USING THE FREERTOS REAL TIME KERNEL[M].2009.
[7] 劉濱,等.嵌入式操作系統FreeRTOS的原理與實現[J].單片機與嵌入式應用,2005(7):8-11.
[8] 陶銳,等.基于ARM7內核的UCoS-Ⅱ移植研究[J].企業技術開發:中旬刊,2012(2):68;74.
作者簡介:
肖 蕾(1974-),男,博士,副教授.研究領域:嵌入式系統,自動化測控技術.
劉克江(1989-),男,本科.研究領域:嵌入式系統.