肖蕾 劉克江
摘 要:在嵌入式系統(tǒng)領(lǐng)域,如何合理地分配和管理系統(tǒng)內(nèi)存RAM資源是程序員必須面對(duì)的問(wèn)題,能否高效、可靠、實(shí)時(shí)地管理動(dòng)態(tài)內(nèi)存分區(qū)決定了整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。本文以μC/OS與FREERTOS兩種操作系統(tǒng)為例,在深入研究其動(dòng)態(tài)內(nèi)存管理機(jī)制的基礎(chǔ)上,對(duì)其優(yōu)缺點(diǎn)和適用場(chǎng)合進(jìn)行了分析比較,便于軟件開(kāi)發(fā)人員在實(shí)際應(yīng)用中根據(jù)產(chǎn)品不同需求進(jìn)行針對(duì)性的選擇。
關(guān)鍵詞:μC/OS FREERTOS;嵌入式系統(tǒng);動(dòng)態(tài)內(nèi)存;管理機(jī)制
中圖分類(lèi)號(hào):TP316.2 文獻(xiàn)標(biāo)識(shí)碼: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)
在嵌入式系統(tǒng)領(lǐng)域中,內(nèi)存RAM一直是一種稀缺資源。如何合理地分配和管理系統(tǒng)的內(nèi)存資源是嵌入式軟件程序員必須面對(duì)的問(wèn)題,特別是在產(chǎn)品必須使用到動(dòng)態(tài)內(nèi)存分配時(shí),能否高效、可靠、實(shí)時(shí)地管理動(dòng)態(tài)內(nèi)存分區(qū)決定了整個(gè)系統(tǒng)的穩(wěn)定性和可靠性[1,2]。
針對(duì)這種情況,程序員常用的解決辦法主要包括下面三種:在系統(tǒng)啟動(dòng)時(shí)就按最壞考慮分配足夠大的數(shù)組、根據(jù)實(shí)際需求自行編寫(xiě)內(nèi)存管理程序或直接使用編譯器提供的malloc和free函數(shù)、基于嵌入式操作系統(tǒng)內(nèi)存管理機(jī)制來(lái)處理。如果程序員能夠充分掌握整個(gè)系統(tǒng)所有可能出現(xiàn)的情況,根據(jù)最壞需求在系統(tǒng)啟動(dòng)時(shí)就給每一項(xiàng)作業(yè)分配一塊足夠大的數(shù)組是最簡(jiǎn)單和直接的方法,但勢(shì)必會(huì)造成內(nèi)存浪費(fèi),且如果作業(yè)需要數(shù)組類(lèi)型是多種的情況很可能會(huì)陷入內(nèi)存空間不足的困境[3]。根據(jù)產(chǎn)品軟件設(shè)計(jì)的需求自行編寫(xiě)簡(jiǎn)單短小的內(nèi)存管理程序,對(duì)于有經(jīng)驗(yàn)程序員來(lái)說(shuō)不成問(wèn)題,但是這種方法也存在著不同平臺(tái)通用性較差、管理程序穩(wěn)定性和可靠性因人而異,另外,malloc和free函數(shù)并不是所有平臺(tái)都可以使用且代碼不可見(jiàn)。移植現(xiàn)成的嵌入式操作系統(tǒng),基于操作系統(tǒng)下的內(nèi)存管理機(jī)制來(lái)處理系統(tǒng)的動(dòng)態(tài)內(nèi)存問(wèn)題是比較方便而且可靠的解決辦法,但程序員必須深入了解所使用操作系統(tǒng)動(dòng)態(tài)內(nèi)存管理機(jī)制的特點(diǎn)和區(qū)別,并能夠針對(duì)不同的處理器資源對(duì)操作系統(tǒng)進(jìn)行一定程度裁剪。
上述三種方法中程序員大多比較傾向于移植現(xiàn)有成熟的嵌入式操作系統(tǒng)來(lái)處理,本文以μC/OS與FREERTOS兩種操作系統(tǒng)為例,在深入研究其動(dòng)態(tài)內(nèi)存管理機(jī)制的基礎(chǔ)上,對(duì)其優(yōu)缺點(diǎn)和適用場(chǎng)合進(jìn)行了分析比較,便于軟件開(kāi)發(fā)人員在實(shí)際應(yīng)用中根據(jù)產(chǎn)品不同需求進(jìn)行針對(duì)性的選擇。
2 內(nèi)存管理算法(Memory management algorithm)
2.1 μC/OS動(dòng)態(tài)內(nèi)存管理
在μC/OS操作系統(tǒng)中,使用動(dòng)態(tài)分配內(nèi)存時(shí)必須先調(diào)用OSMemCreate函數(shù)建立并初始化一個(gè)內(nèi)存區(qū),該內(nèi)存區(qū)會(huì)被分割成n塊固定大小的內(nèi)存塊。OSMemCreate函數(shù)傳遞參數(shù)指定內(nèi)存區(qū)起始地址、每塊內(nèi)存塊大小以及內(nèi)存塊數(shù)量。初始化后的結(jié)構(gòu)在每一塊空閑塊開(kāi)頭存放著指向下一塊空閑塊指針。初始化后μC/OS為每一個(gè)動(dòng)態(tài)內(nèi)存區(qū)定義一個(gè)“內(nèi)存控制塊”來(lái)記錄和跟蹤該區(qū)的使用情況,包含內(nèi)存分區(qū)指針、空閑塊鏈表指針、每塊內(nèi)存大小、內(nèi)存塊數(shù)目和空閑內(nèi)存塊數(shù)目,其結(jié)構(gòu)為:
typedef struct
﹛
void *OSMemAddr;
void *OSMemFreeList;
INT32U OSMemBlkSize;
INT32U OSMemNBlks;
INT32U OSMemNFree;
﹜OS_MEM;
當(dāng)用戶(hù)程序調(diào)用函數(shù)OSMemGet申請(qǐng)一塊動(dòng)態(tài)內(nèi)存時(shí),系統(tǒng)便通過(guò)“內(nèi)存控制塊”將空閑塊鏈表指向的第一塊空閑內(nèi)存塊分配給程序,同時(shí)將空閑塊鏈表指針指向下一個(gè)空閑塊并更新空閑塊數(shù)目。在程序需要釋放內(nèi)存時(shí)調(diào)用OSMemPut函數(shù),系統(tǒng)根據(jù)“內(nèi)存控制塊”把回收的內(nèi)存塊插入到空閑塊鏈表表頭,并更新空閑塊數(shù)目。由此可見(jiàn),μC/OS每次分配和回收動(dòng)態(tài)內(nèi)存的時(shí)間是確定的,每一次分配和回收的內(nèi)存大小已知,且沒(méi)有內(nèi)存碎片的存在。
μC/OS為了保證內(nèi)存管理時(shí)間確定性和解決內(nèi)存碎片問(wèn)題,在用戶(hù)程序初始化一塊動(dòng)態(tài)內(nèi)存區(qū)時(shí)便指定了該內(nèi)存區(qū)每一塊內(nèi)存塊長(zhǎng)度,申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí)只能得到初始化時(shí)指定的大小,OSMemGet這個(gè)函數(shù)并不需要用戶(hù)程序指定需要申請(qǐng)的內(nèi)存長(zhǎng)度,內(nèi)存塊長(zhǎng)度由圖2內(nèi)存控制塊中的OSMemBlkSize決定。如果出現(xiàn)多個(gè)需要?jiǎng)討B(tài)使用內(nèi)存的任務(wù),且每個(gè)任務(wù)所需的內(nèi)存塊長(zhǎng)度都不一樣,程序員可以多次調(diào)用OSMemCreate函數(shù)創(chuàng)建包含不同大小內(nèi)存塊的動(dòng)態(tài)內(nèi)存區(qū),最大創(chuàng)建的內(nèi)存區(qū)數(shù)目可通過(guò)OS_MAX_MEM_PART設(shè)定,然后程序需要多大的內(nèi)存塊就到對(duì)應(yīng)內(nèi)存控制塊中申請(qǐng)即可;另一種做法就是調(diào)用OSMemCreate時(shí)根據(jù)最大內(nèi)存塊大小去初始化一塊動(dòng)態(tài)內(nèi)存區(qū),所有程序都在該內(nèi)存區(qū)中申請(qǐng)和釋放動(dòng)態(tài)內(nèi)存,在調(diào)用OSMemGet得到一塊較大的內(nèi)存塊時(shí)強(qiáng)制轉(zhuǎn)換成具體程序所需的數(shù)據(jù)格式即可。
通過(guò)分析可知μC/OS處理動(dòng)態(tài)內(nèi)存的方法具有如下優(yōu)點(diǎn):
(1)μC/OS對(duì)動(dòng)態(tài)內(nèi)存分區(qū)的管理機(jī)制在操作上時(shí)間是可確定的。
(2)不會(huì)產(chǎn)生所謂動(dòng)態(tài)內(nèi)存碎片,可以最大限度保證系統(tǒng)的穩(wěn)定性和可靠性。
(3)每一動(dòng)態(tài)內(nèi)存塊大小固定,每個(gè)空閑內(nèi)存塊頂部只需存儲(chǔ)下一個(gè)空閑塊指針,減小了系統(tǒng)額外開(kāi)支。
但是,該算法的缺點(diǎn)也是顯而易見(jiàn)的:
(1)不能靈活充分利用整個(gè)內(nèi)存空間。無(wú)論是創(chuàng)建多個(gè)內(nèi)存區(qū)還是根據(jù)最大內(nèi)存塊創(chuàng)建一個(gè)內(nèi)存區(qū),都會(huì)造成嚴(yán)重的內(nèi)存空間浪費(fèi)。
(2)缺乏靈活性。用戶(hù)程序之所以使用動(dòng)態(tài)內(nèi)存分配目的便是為了提高靈活性,但是μC/OS在初始化動(dòng)態(tài)內(nèi)存區(qū)時(shí)便將其劃分為固定大小的連續(xù)存取區(qū),這樣在某些時(shí)候不能確定某個(gè)內(nèi)存塊大小時(shí)便無(wú)法通過(guò)該算法解決問(wèn)題。
(3)不檢查回收內(nèi)存塊的合法性。μC/OS的內(nèi)存回收函數(shù)OSMemPut在回收內(nèi)存時(shí)并不檢查所回收的內(nèi)存塊是否是本動(dòng)態(tài)內(nèi)存區(qū)的空間,用戶(hù)程序調(diào)用OSMemPut函數(shù)時(shí)傳遞任何參數(shù),只要內(nèi)存控制塊中OSMemNFree小于OSMemNBlks便將該參數(shù)指向的空間作為空閑塊鏈表節(jié)點(diǎn)插入到空閑塊鏈表中,這種情況導(dǎo)致的后果是將是不可預(yù)估的。
2.2 FreeRTOS動(dòng)態(tài)內(nèi)存管理
FreeRTOS是一個(gè)微型嵌入式操作系統(tǒng)內(nèi)核,具有源碼公開(kāi)、免費(fèi)、可裁剪、調(diào)度策略靈活和簡(jiǎn)單易用等特點(diǎn),被很多嵌入式開(kāi)發(fā)人員所選用[4,5]。對(duì)于內(nèi)存管理,F(xiàn)reeRTOS根據(jù)使用者實(shí)際需求提供三種策略,每種策略對(duì)應(yīng)獨(dú)立的源文件,需要將對(duì)應(yīng)的文件移植到工程中[6-8]。
策略一是三個(gè)方案中最簡(jiǎn)單的,系統(tǒng)根據(jù)onfigTOTAL_HEAP_SIZE設(shè)定的大小劃分一塊內(nèi)存作為動(dòng)態(tài)內(nèi)存區(qū),同時(shí)定義變量xNextFreeByte標(biāo)志空閑區(qū)域的位置,初始值為0。當(dāng)用戶(hù)程序申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí)便返回當(dāng)前xNextFreeByte代表的內(nèi)存地址,并將xNextFreeByte加上所申請(qǐng)內(nèi)存塊長(zhǎng)度,且內(nèi)存一旦分配便不允許釋放,策略一中沒(méi)有提供內(nèi)存回收方法。
策略二中建立空閑分區(qū)鏈表采用首次適應(yīng)算法分配動(dòng)態(tài)內(nèi)存,允許分配后的動(dòng)態(tài)內(nèi)存調(diào)用釋放函數(shù)進(jìn)行回收,然而,它不具備將鄰近空閑塊合并成一個(gè)大空閑塊的功能。FreeRTOS為動(dòng)態(tài)內(nèi)存分區(qū)中每一塊空閑塊建立一個(gè)“空閑分區(qū)節(jié)點(diǎn)”,并將該節(jié)點(diǎn)存放于空閑塊頂部。該節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)包含指向下一節(jié)點(diǎn)指針和本空閑分區(qū)大小。同時(shí)定義一個(gè)開(kāi)始節(jié)點(diǎn)和終止節(jié)點(diǎn)作為空閑分區(qū)鏈表的表頭和結(jié)尾,當(dāng)用戶(hù)程序調(diào)用函數(shù)pvPortMalloc申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí),便從空閑分區(qū)鏈表表頭節(jié)點(diǎn)開(kāi)始查找合適大小的內(nèi)存塊(即該空閑內(nèi)存分區(qū)大于或等于所申請(qǐng)內(nèi)存),找到則返回該空閑分區(qū)存儲(chǔ)地址并修改該塊“空閑分區(qū)節(jié)點(diǎn)”內(nèi)容,判斷該塊剩余空間是否可以創(chuàng)建“空閑分區(qū)節(jié)點(diǎn)”,可以則將該塊剩余空間劃分為新的空閑塊并建立新的“空閑分區(qū)節(jié)點(diǎn)”,最后更新空閑分區(qū)鏈表,F(xiàn)reeRTOS空閑分區(qū)節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)如下所示。
typedef struct A_BLOCK_LINK
﹛
struct A_BLOCK_LINK *pxNextFreeBlock;
size_t xBlockSize;
﹜xBlockLink;
通過(guò)上述分析可以得出FreeRTOS策略二具有下列優(yōu)點(diǎn):
(1)根據(jù)用戶(hù)程序申請(qǐng)的每一塊動(dòng)態(tài)內(nèi)存大小建立一個(gè)空閑分區(qū)節(jié)點(diǎn)記錄該動(dòng)態(tài)內(nèi)存的信息,真正意義上實(shí)現(xiàn)了動(dòng)態(tài)分配。
(2)用戶(hù)不必在系統(tǒng)啟動(dòng)時(shí)初始化動(dòng)態(tài)內(nèi)存區(qū),對(duì)外接口函數(shù)只有pvPortMalloc和vPortFree,很大程度上降低了使用動(dòng)態(tài)內(nèi)存的難度。
(3)系統(tǒng)采用首次適應(yīng)算法減小了分配和回收動(dòng)態(tài)內(nèi)存時(shí)的查找時(shí)間。
同樣,該策略也存在著如下缺點(diǎn):
(1)每次分配和回收動(dòng)態(tài)內(nèi)存的時(shí)間不固定,即存在著時(shí)間不確定性。
(2)如果用戶(hù)程序需要頻繁分配和回收大小不同的動(dòng)態(tài)內(nèi)存塊時(shí),隨著系統(tǒng)運(yùn)行,可能會(huì)出現(xiàn)空閑分區(qū)鏈表越來(lái)越大,整個(gè)動(dòng)態(tài)內(nèi)存分區(qū)會(huì)被分割成很多個(gè)細(xì)小的內(nèi)存碎片,且每一個(gè)內(nèi)存碎片都附帶一個(gè)空閑分區(qū)節(jié)點(diǎn),造成內(nèi)存空間的大量浪費(fèi)。
(3)一旦出現(xiàn)(2)所述的現(xiàn)象,整個(gè)系統(tǒng)的穩(wěn)定性將會(huì)降低,甚至?xí)l(fā)系統(tǒng)的崩潰,盡管這種情況并不是開(kāi)發(fā)人員編程邏輯算法錯(cuò)誤造成的,但是FreeRTOS并沒(méi)提供方法或者試圖去阻止這種情況發(fā)生。
(4)FreeRTOS在回收內(nèi)存時(shí)同樣沒(méi)有檢查用戶(hù)程序所釋放內(nèi)存塊的合法性,這是因?yàn)镕reeRTOS采用從小到大排列空閑分區(qū)塊,這樣便找不到一種很好的算法去判別所釋放內(nèi)存塊是否屬于動(dòng)態(tài)內(nèi)存區(qū)和該動(dòng)態(tài)內(nèi)存塊結(jié)構(gòu)有沒(méi)有遭到破壞。
策略三只是對(duì)標(biāo)準(zhǔn)malloc()和free()函數(shù)線(xiàn)程安全方面的進(jìn)行包裝。具體做法是當(dāng)用戶(hù)程序調(diào)用pvPortMalloc申請(qǐng)動(dòng)態(tài)內(nèi)存的時(shí)候,暫停系統(tǒng)任務(wù)調(diào)度,將pvPortMalloc的參數(shù)傳給malloc函數(shù),最后開(kāi)啟任務(wù)調(diào)度,并將malloc返回值傳遞給用戶(hù)程序,在釋放內(nèi)存時(shí)調(diào)用vPortFree所做步驟也是一樣的。
3 結(jié)論(Conclusion)
通過(guò)對(duì)μC/OS與FREERTOS兩個(gè)常用小型嵌入式操作系統(tǒng)內(nèi)存管理機(jī)制的分析與比較可知:μC/OS動(dòng)態(tài)內(nèi)存管理機(jī)制將系統(tǒng)實(shí)時(shí)性和可靠性放在第一位,甚至犧牲了內(nèi)存空間和動(dòng)態(tài)內(nèi)存分配的靈活性以換取絕對(duì)確定的分配和回收時(shí)間,如果產(chǎn)品實(shí)時(shí)性和可靠性要求很高,有足夠內(nèi)存空間可供使用,對(duì)于動(dòng)態(tài)內(nèi)存分配情況在設(shè)計(jì)初始便能確定的情況下,μC/OS動(dòng)態(tài)內(nèi)存管理機(jī)制便是首選了;反之如果用戶(hù)程序?qū)?dòng)態(tài)內(nèi)存分配和回收情況無(wú)法確定或者所申請(qǐng)大小是隨機(jī)的,由于控制成本導(dǎo)致沒(méi)有足夠大內(nèi)存空間可供使用,且要求動(dòng)態(tài)內(nèi)存管理機(jī)制具備很高的靈活性,這樣便只能選擇實(shí)時(shí)性和可靠性相對(duì)不足的FreeRTOS系統(tǒng)。
本文以μC/OS與FREERTOS兩種操作系統(tǒng)為例,在深入研究其動(dòng)態(tài)內(nèi)存管理機(jī)制的基礎(chǔ)上,對(duì)其優(yōu)缺點(diǎn)和適用場(chǎng)合進(jìn)行了分析比較,說(shuō)明了產(chǎn)品軟件設(shè)計(jì)階段動(dòng)態(tài)內(nèi)存分配機(jī)制的選擇在一定程度上左右著整個(gè)系統(tǒng)的成本和性能,在低成本嵌入式產(chǎn)品中使用到動(dòng)態(tài)內(nèi)存,必須根據(jù)產(chǎn)品的實(shí)際情況選擇合適的動(dòng)態(tài)內(nèi)存管理算法,便于軟件開(kāi)發(fā)人員在實(shí)際應(yīng)用中根據(jù)產(chǎn)品不同需求進(jìn)行針對(duì)性的選擇。
參考文獻(xiàn)(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] 黃鵬程.嵌入式實(shí)時(shí)操作系統(tǒng)FreeRTOS在ARM7上移植的實(shí)現(xiàn)[J].中國(guó)電子商情:通信市場(chǎng),2009(3):59-64.
[5] 張龍彪,張果.嵌入式操作系統(tǒng)FreeRTOS的原理與移植實(shí)現(xiàn)[J].信息技術(shù),2012(11):31-34.
[6] Richard Barry.USING THE FREERTOS REAL TIME KERNEL[M].2009.
[7] 劉濱,等.嵌入式操作系統(tǒng)FreeRTOS的原理與實(shí)現(xiàn)[J].單片機(jī)與嵌入式應(yīng)用,2005(7):8-11.
[8] 陶銳,等.基于ARM7內(nèi)核的UCoS-Ⅱ移植研究[J].企業(yè)技術(shù)開(kāi)發(fā):中旬刊,2012(2):68;74.
作者簡(jiǎn)介:
肖 蕾(1974-),男,博士,副教授.研究領(lǐng)域:嵌入式系統(tǒng),自動(dòng)化測(cè)控技術(shù).
劉克江(1989-),男,本科.研究領(lǐng)域:嵌入式系統(tǒng).