rt-thread的內(nèi)存管理分析
掃描二維碼
隨時(shí)隨地手機(jī)看文章
rt-thread的內(nèi)存管理分析
-
1.概述
-
2.靜態(tài)內(nèi)存與動(dòng)態(tài)內(nèi)存
-
3.小內(nèi)存管理
-
4.slab內(nèi)存管理
-
4.1 rt-thread上slab基本介紹
-
4.2 rt-thread上slab內(nèi)存的管理
-
5.memheap 管理算法
-
6.內(nèi)存池管理算法
-
7.總結(jié)
1.概述
內(nèi)存是計(jì)算機(jī)中十分重要的資源。隨著芯片性能的提升,容量的變大,內(nèi)存資源的管理顯得非常重要。內(nèi)存管理是操作系統(tǒng)中一個(gè)基本功能,一般操作系統(tǒng)的功能可以概括為五個(gè)部分:處理器管理、內(nèi)存管理、任務(wù)管理、I/O設(shè)備管理、文件管理。對(duì)于嵌入式操作系統(tǒng),一個(gè)好的內(nèi)存管理策略,將大大提高系統(tǒng)的性能,對(duì)系統(tǒng)穩(wěn)定性也至關(guān)重要。
本文主要從RT-Thread的內(nèi)存管理策略的角度出發(fā),梳理一下目前RT-Thread系統(tǒng)中的內(nèi)存管理,同時(shí)從實(shí)際應(yīng)用的角度出發(fā),選擇合適的方案進(jìn)行內(nèi)存管理。
2.靜態(tài)內(nèi)存與動(dòng)態(tài)內(nèi)存
一般來說,靜態(tài)內(nèi)存就是系統(tǒng)在啟動(dòng)之前,已經(jīng)獲得了系統(tǒng)運(yùn)行的所有內(nèi)存。不需要在程序運(yùn)行的時(shí)候,另外進(jìn)行內(nèi)存的分配。這種方式使用內(nèi)存,一般實(shí)時(shí)性比較強(qiáng),省去了單獨(dú)分配內(nèi)存的時(shí)間。但是由于是靜態(tài)內(nèi)存,使用的效率比較低下,不用單獨(dú)進(jìn)行管理。
動(dòng)態(tài)內(nèi)存就是通過分配(malloc)和釋放(free)對(duì)內(nèi)存進(jìn)行一定的管理,這種內(nèi)存的使用效率比較高,但反復(fù)的申請(qǐng)與釋放,容易產(chǎn)生內(nèi)存碎片的問題。
對(duì)于RT-Thread的內(nèi)存管理方式,主要從小內(nèi)存管理、slab、memheap以及內(nèi)存池這四種內(nèi)存管理策略上去分析對(duì)比,從而選擇最佳的內(nèi)存管理策略。
3.小內(nèi)存管理
rt-thread的小內(nèi)存管理算法是一種比較簡單的內(nèi)存分配管理算法。應(yīng)用的場(chǎng)景在內(nèi)存在1MB以下使用比較合適。其原理是當(dāng)需要分配時(shí),從內(nèi)存堆上分配出一塊內(nèi)存供系統(tǒng)使用,后面需要的時(shí)候,接著從后面空閑的內(nèi)存區(qū)域進(jìn)行分配。
當(dāng)調(diào)用rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END);會(huì)分配出一段堆空間,其中l(wèi)free指向這段堆空間的頭部位置。
此時(shí)分配一個(gè)32字節(jié)的數(shù)據(jù),會(huì)首先申請(qǐng)一個(gè)內(nèi)存的頭部信息,這個(gè)頭部信息為12字節(jié)。
heap_mem一般為12字節(jié)。
#define HEAP_MAGIC 0x1ea0 struct heap_mem { /* magic and used flag */ rt_uint16_t magic; rt_uint16_t used; #ifdef ARCH_CPU_64BIT rt_uint32_t resv; #endif rt_size_t next, prev; #ifdef RT_USING_MEMTRACE #ifdef ARCH_CPU_64BIT rt_uint8_t thread[8]; #else rt_uint8_t thread[4]; /* thread name */ #endif #endif };
其中magic為魔數(shù),為0x1ea0,英文為heap。rt_size_t數(shù)據(jù)類型為rt_ubase_t。在32位程序中為4字節(jié)。所以合起來,頭部字節(jié)的大小為12字節(jié)。此時(shí)依次分配50字節(jié)和64字節(jié)。內(nèi)存布局如下所示:
由于lfree永遠(yuǎn)指向第一個(gè)空閑的內(nèi)存的起始地址,所以此時(shí)lfree在為分配的內(nèi)存塊后面。依次向后去分配合適的內(nèi)存塊大小。
此時(shí)如果釋放了一個(gè)50字節(jié)的內(nèi)存塊。此時(shí)的內(nèi)存布局如下:
由于空間被釋放,所以導(dǎo)致內(nèi)存的中間有一塊空閑的區(qū)域。此時(shí)如果要申請(qǐng)25字節(jié),那么可以在空閑的這個(gè)區(qū)域里面找到。但是如果此時(shí)申請(qǐng)100字節(jié)的時(shí)候,那么導(dǎo)致空閑的區(qū)域的內(nèi)存不夠了,接著向后面去找空閑的大內(nèi)存片段。
此時(shí),會(huì)向后面的大區(qū)域去申請(qǐng)內(nèi)存塊,導(dǎo)致前面的內(nèi)存區(qū)域被空出來了。如果在內(nèi)存中間有很多這種小的空余的片段出現(xiàn),那么一定會(huì)導(dǎo)致內(nèi)存碎片的問題。
這種方式內(nèi)存使用的比較簡單,適合內(nèi)存比較小的情況下使用,并且操作內(nèi)存的申請(qǐng)和釋放不要太頻繁的情況下去使用比較理想。一般在內(nèi)存為1M以下的情況下使用這種算法比較理想。
4.slab內(nèi)存管理
4.1 rt-thread上slab基本介紹
RT-Thread 的 slab 分配器是在 DragonFly BSD 創(chuàng)始人 Matthew Dillon 實(shí)現(xiàn)的 slab 分配器基礎(chǔ)上,針對(duì)嵌入式系統(tǒng)優(yōu)化的內(nèi)存分配算法。最原始的 slab 算法是 Jeff Bonwick 為 Solaris 操作系統(tǒng)而引入的一種高效內(nèi)核內(nèi)存分配算法。
RT-Thread 的 slab 分配器實(shí)現(xiàn)主要是去掉了其中的對(duì)象構(gòu)造及析構(gòu)過程,只保留了純粹的緩沖型的內(nèi)存池算法。
在理解slab分配器的時(shí)候,需要理解兩個(gè)概念:ZONE、CHUNK。
CHUNK:
chunk是一段固定大小的空間,一般來說,slab分配器是支持72種不同大小的chunk。數(shù)據(jù)量分別如下:
8、16、24、32、40、48、56、64、72、80、88、96、104、112、120、128、144、160、176、192、208、224、240、256、288、320、352、384、416、448、480、512、576、640、704、770、834、898、962、1024、1152、1280、1408、1536、1664、1792、1920、2048、2304、2560、2816、3072、3328、3584、3840、4096、4608、5120、5632、6144、6656、7168、7680、8192、9216、10240、11264、12288、13312、14336、15360、16384
一般來說,slab分配器只會(huì)去尋找上述可以分配的空間,如果分配的chunk大小不相等,那么會(huì)通過zoneindex函數(shù)去找到最合適的可以分配的內(nèi)存的chunk大小。
ZONE:
每個(gè)zone有范圍從32k~128K的字節(jié),每個(gè)zone里都包含相同大小的chunk。
#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */ #define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */
一般來說,在初始化的時(shí)候,進(jìn)行void rt_system_heap_init(void *begin_addr, void *end_addr)操作的時(shí)候,會(huì)根據(jù)給系統(tǒng)的heap空間,動(dòng)態(tài)的調(diào)整zone的大小。
/* calculate zone size */ zone_size = ZALLOC_MIN_ZONE_SIZE;//32K while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize / 1024)) zone_size <<= 1;
根據(jù)上述的調(diào)整方式,可以算出,當(dāng)heap空間在64M及以下時(shí),zone的大小都為32K。當(dāng)系統(tǒng)的heap空間在256M及以上時(shí),zone的大小為最大128K。這個(gè)值在兩者中間時(shí)是動(dòng)態(tài)進(jìn)行調(diào)整的。
按照上述的介紹,可以看出這兩者的關(guān)系如圖所示:
4.2 rt-thread上slab內(nèi)存的管理
采用slab算法的時(shí)候,主要有兩種情況:
1.底層的頁分配器
2.上層的slab分配器
其中頁分配器是用page分配的一個(gè)片段。在宏定義中RT_MM_PAGE_SIZE表示page大小,一般定義的大小為4K。
初始化的時(shí)候,會(huì)將zone_array[NZONES]會(huì)被置為空,表示沒有可以用的zone。例如此時(shí)申請(qǐng)一個(gè)30字節(jié)的數(shù)據(jù)空間。當(dāng)調(diào)用rt_malloc(30),那么會(huì)通過zoneindex函數(shù)找到一個(gè)合適的32字節(jié)的chunk。找到zone的下標(biāo)為3,查詢到zone_array[3]為空。
(1)當(dāng)發(fā)現(xiàn)zone_array[3]為空時(shí),會(huì)通過rt_page_alloc去分配page。假如動(dòng)態(tài)調(diào)整的zone大小為128K。需要分配出32個(gè)page,一個(gè)page的大小為4K。32*4K=128K。
(2)此時(shí)當(dāng)前zone中的內(nèi)存布局如下:
由于第一次分配時(shí),可以分配到32字節(jié)的chunk1,并標(biāo)記chunk1被使用,然后下次申請(qǐng)符合這個(gè)chunk條件的時(shí)候直接可以申請(qǐng)到chunk2。如果中間被釋放了,那么就可以被其他申請(qǐng)到了。
如果該zone上的所有的chunk都申請(qǐng)完了,那么可以申請(qǐng)同等類型的zone,繼續(xù)這樣操作。
使用zone管理,可以從一定意義上避免內(nèi)存片段的問題,但是內(nèi)存管理起來比較的麻煩,比較耗時(shí)。適合內(nèi)存比較大,并且需要頻繁申請(qǐng)和釋放內(nèi)存的時(shí)候使用。
5.memheap 管理算法
memheap 管理算法適用于系統(tǒng)含有多個(gè)地址可不連續(xù)的內(nèi)存堆。使用 memheap 內(nèi)存管理可以簡化系統(tǒng)存在多個(gè)內(nèi)存堆時(shí)的使用:當(dāng)系統(tǒng)中存在多個(gè)內(nèi)存堆的時(shí)候,用戶只需要在系統(tǒng)初始化時(shí)將多個(gè)所需的 memheap 初始化,并開啟 memheap 功能就可以很方便地把多個(gè) memheap(地址可不連續(xù))粘合起來用于系統(tǒng)的 heap 分配。
這種內(nèi)存分配的特點(diǎn)在于可以把不連續(xù)的空間變得連續(xù)起來。具體的使用方法就是當(dāng)開啟了RT_USING_MEMHEAP_AS_HEAP這個(gè)宏定義,第一次需要使用rt_system_heap_init分配堆空間,然后需要用戶定義一個(gè)struct rt_memheap結(jié)構(gòu)體靜態(tài)變量,然后再使用rt_memheap_init函數(shù)初始化。這樣可以把兩端不連續(xù)的空間作為一塊連續(xù)的空間來管理。具體來說,比如sram與sdram的地址管理。
關(guān)于內(nèi)存的管理部分,可以查看小內(nèi)存管理部分。
6.內(nèi)存池管理算法
在使用操作系統(tǒng)做業(yè)務(wù)邏輯時(shí),往往有這樣一種需求。比如做通信協(xié)議,或者使用動(dòng)態(tài)內(nèi)存比較多的情況。這時(shí)候可以在內(nèi)存堆上單獨(dú)分配一塊內(nèi)存作為具體業(yè)務(wù)邏輯的內(nèi)存池使用。
做內(nèi)存管理的時(shí)候,調(diào)用下面函數(shù)去創(chuàng)建一個(gè)內(nèi)存池。
rt_mp_t rt_mp_create(const char* name, rt_size_t block_count, rt_size_t block_size);
內(nèi)存池中會(huì)分配出一些列的內(nèi)存塊。當(dāng)實(shí)際使用時(shí),如果申請(qǐng)內(nèi)存池中的數(shù)據(jù)申請(qǐng)不到了,那么無法分配,會(huì)使得線程掛起,等待釋放內(nèi)存池中的數(shù)據(jù)塊時(shí),會(huì)得到內(nèi)存塊。
采用內(nèi)存池的好處是自己的任務(wù)用自己的內(nèi)存,不會(huì)造成頻繁分配釋放造成的內(nèi)存碎片問題。
7.總結(jié)
內(nèi)存管理是操作系統(tǒng)中比較重要的一個(gè)部分,使用得當(dāng),會(huì)大大提高系統(tǒng)軟件的質(zhì)量。rt-thread提供的小內(nèi)存管理、slab、memheap、內(nèi)存池這些內(nèi)存管理策略,一方面針對(duì)不同大小,不同用途的內(nèi)存做了區(qū)分,可以選擇合適的內(nèi)存管理策略對(duì)內(nèi)存進(jìn)行使用。想要使用好rtos操作系統(tǒng),需要不斷的思考內(nèi)存的使用方式,靜態(tài)內(nèi)存、動(dòng)態(tài)內(nèi)存、全局變量、局部變量、臨界區(qū)等等、在注意這些問題的條件下去做設(shè)計(jì),才能設(shè)計(jì)出好的系統(tǒng)架構(gòu),才能做出更好的穩(wěn)定可靠的產(chǎn)品。
- END -