前言
本文講RT-Thread的內存管理,包括為何不使用C標準庫的內存管理函數、內存管理的特點、RT-Thread 程序內存分布、內存堆管理、內存池管理以及使用STM32進行實驗。
一、不直接使用 C 標準庫中的內存管理函數的原因
很多人會有疑問,為什么不直接使用 C 標準庫中的內存管理函數呢?在電腦中我們可以用malloc()和 free()這兩個函數動態的分配內存和釋放內存。但是,在嵌入式實時操作系統中,調用 malloc()和 free()卻是危險的,原因有以下幾點:
1、這些函數在小型嵌入式系統中并不總是可用的,小型嵌入式設備中的 RAM 不足。
2、它們的實現可能非常的大,占據了相當大的一塊代碼空間。
3、他們幾乎都不是線程安全的。
4、它們并不是確定的,每次調用這些函數執行的時間可能都不一樣。
5、它們有可能產生碎片。
6、這兩個函數會使得鏈接器配置得復雜。
7、如果允許堆空間的生長方向覆蓋其他變量占據的內存,它們會成為 debug 的災難 。
二、內存管理的功能特點
1、分配內存的時間必須是確定的。一般內存管理算法是根據需要存儲的數據的長度在內存中去尋找一個與這段數據相適應的空閑內存塊,然后將數據存儲在里面。而尋找這樣一個空閑內存塊所耗費的時間是不確定的,因此對于實時系統來說,這就是不可接受的,實時系統必須要保證內存塊的分配過程在可預測的確定時間內完成,否則實時任務對外部事件的響應也將變得不可確定。
2、隨著內存不斷被分配和釋放,整個內存區域會產生越來越多的碎片(因為在使用過程中,申請了一些內存,其中一些釋放了,導致內存空間中存在一些小的內存塊,它們地址不連續,不能夠作為一整塊的大內存分配出去),系統中還有足夠的空閑內存,但因為它們地址并非連續,不能組成一塊連續的完整內存塊,會使得程序不能申請到大的內存。對于通用系統而言,這種不恰當的內存分配算法可以通過重新啟動系統來解決 (每個月或者數個月進行一次),但是對于那些需要常年不間斷地工作于野外的嵌入式系統來說,就變得讓人無法接受了。
3、嵌入式系統的資源環境也是不盡相同,有些系統的資源比較緊張,只有數十 KB 的內存可供分配,而有些系統則存在數 MB 的內存,如何為這些不同的系統,選擇適合它們的高效率的內存分配算法,就將變得復雜化。
三、RT-Thread 程序內存分布
一般 MCU 包含的存儲空間有:片內 Flash 與片內 RAM,RAM 相當于內存,Flash 相當于硬盤。
上面提到的 Program Size 包含以下幾個部分:
(1)Code:代碼段,存放程序的代碼部分;
(2)RO-data:只讀數據段,存放程序中定義的常量;
(3)RW-data:讀寫數據段,存放初始化為非 0 值的全局變量;
(4)ZI-data:0 數據段,存放未初始化的全局變量及初始化為 0 的變量;
編譯完工程會生成一個. map 的文件,該文件說明了各個函數占用的尺寸和地址,在文件的最后幾行也說明了上面幾個字段的關系:
1TotalROSize(Code+ROData)43688(42.66kB)2TotalRWSize(RWData+ZIData)3976(3.88kB)3TotalROMSize(Code+ROData+RWData)43812(42.79kB)4
2、程序運行之前,需要有文件實體被燒錄到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,該被燒錄文件稱為可執行映像文件。如圖下圖 中左圖所示,是可執行映像文件燒錄到 STM32 后的內存分布,它包含 RO 段和 RW 段兩個部分:其中 RO 段中保存了 Code、RO-data 的數據,RW 段保存了 RW-data 的數據,由于 ZI-data 都是 0,所以未包含在映像文件中。
RT-Thread 內存分布(來源RT-Thread編程指南)
3、STM32 在上電啟動之后默認從 Flash 啟動,啟動之后會將 RW 段中的 RW-data(初始化的全局變量)搬運到 RAM 中,但不會搬運 RO 段,即 CPU 的執行代碼從 Flash 中讀取,另外根據編譯器給出的 ZI 地址和大小分配出 ZI 段,并將這塊 RAM 區域清零。
四、內存堆管理
內存堆管理根據具體內存設備劃分為三種情況:(1)針對小內存塊的分配管理(小內存管理算法);(2)針對大內存塊的分配管理(slab 管理算法);(3)針對多內存堆的分配情況(memheap 管理算法)
1、將 *“ZI 段結尾處”* 到內存尾部的空間用作內存堆
(1)內存堆管理用于管理一段連續的內存空間如下圖所示,RT-Thread 將 “ZI 段結尾處” 到內存尾部的空間用作內存堆。
RT-Thread 內存分布(來源RT-Thread編程指南)
(2)在前面的其他筆記,都是從內部SRAM申請一塊靜態內存來作為內存使用。
1#ifdefined(RT_USING_USER_MAIN)&&defined(RT_USING_HEAP) 2#defineRT_HEAP_SIZE6*1024 3/*從內部SRAM申請一塊靜態內存來作為內存堆使用*/ 4staticuint32_trt_heap[RT_HEAP_SIZE];//heapdefaultsize:24K(1024*4*6) 5 6RT_WEAKvoid*rt_heap_begin_get(void) 7{ 8returnrt_heap; 9}1011RT_WEAKvoid*rt_heap_end_get(void)12{13returnrt_heap+RT_HEAP_SIZE;14}15#endif161718/*在rt_hw_board_init中*/1920rt_system_heap_init(rt_heap_begin_get(),rt_heap_end_get());
(3)那么接下來,我們修改代碼,將 “ZI 段結尾處” 到內存尾部的空間用作內存堆。
(A)在board.h添加如下代碼:
1#ifdef__ICCARM__ 2//Use*.icframsymbal,toavoidhardcode. 3externchar__ICFEDIT_region_IRAM1_end__; 4#defineSTM32_SRAM_END&__ICFEDIT_region_IRAM1_end__ 5#else 6#defineSTM32_SRAM_SIZE96/*根據自己的MCU不同修改*/ 7#defineSTM32_SRAM_END(0x20000000+STM32_SRAM_SIZE*1024)/*根據自己的MCU不同修改*/ 8#endif 910#ifdef__CC_ARM11externintImage$$RW_IRAM1$$ZI$$Limit;12#defineHEAP_BEGIN(&Image$$RW_IRAM1$$ZI$$Limit)13#elif__ICCARM__14#pragmasection="HEAP"15#defineHEAP_BEGIN(__segment_end("HEAP"))16#else17externint__bss_end;18#defineHEAP_BEGIN(&__bss_end)19#endif2021#defineHEAP_ENDSTM32_SRAM_END
(B)在board.c中將前面第(2)的那部分代碼全部去掉,然后修改rt_hw_board_init函數,在后面加入如下代碼:
1#ifdefined(RT_USING_USER_MAIN)&&defined(RT_USING_HEAP)2rt_system_heap_init((void*)HEAP_BEGIN,(void*)HEAP_END);3#endif
2、小內存管理算法
(1)小內存管理算法是一個簡單的內存分配算法。初始時,它是一塊大的內存,其大小為(MEM_SIZE)。
初始時的內存(來源[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》)
(2)當需要分配內存塊時,將從這個大的內存塊上分割出相匹配的內存塊,然后把分割出來的空閑內存塊還回給堆管理系統中。每個內存塊都包含一個管理用的數據頭,通過這個頭把使用塊與空閑塊用雙向鏈表的方式鏈接起來(內存塊鏈表)。
小內存管理工作機制圖(來源[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》)
(3)每個內存塊(不管是已分配的內存塊還是空閑的內存塊)都包含一個數據頭,其中包括:
(A)magic:變數(或稱為幻數),它會被初始化成 0x1ea0(即英文單詞 heap),用于標記這個內存塊是一個內存管理用的內存數據塊;變數不僅僅用于標識這個數據塊是一個內存管理用的內存數據塊,實質也是一個內存保護字:如果這個區域被改寫,那么也就意味著這塊內存塊被非法改寫(正常情況下只有內存管理器才會去碰這塊內存)。
(B)used:指示出當前內存塊是否已經分配。
(4)內存管理的在表現主要體現在內存的分配與釋放上,小型內存管理算法可以用以下例子體現出來??臻e鏈表指針 lfree 初始指向 32 字節的內存塊。當用戶線程要再分配一個 64 字節的內存塊時,但此 lfree 指針指向的內存塊只有 32 字節并不能滿足要求,內存管理器會繼續尋找下一內存塊,當找到再下一塊內存塊,128 字節時,它滿足分配的要求。因為這個內存塊比較大,分配器將把此內存塊進行拆分,余下的內存塊(52字節)繼續留在 lfree鏈表中,在每次分配內存塊前,都會留出 12 字節數據頭用于 magic,used 信息及鏈表節點使用。返回給應用的地址實際上是這塊內存塊 12 字節以后的地址,而數據頭部分是用戶永遠不應該改變的部分。
小內存管理算法鏈表結構示意圖(來源[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》)
分配 64 字節后的鏈表結構(來源[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》)
(5)釋放時則是相反的過程,分配器會查看前后相鄰的內存塊是否空閑,如果空閑則合并成一個大的空閑內存塊。
3、slab 管理算法
RT-Thread 的 slab 分配器是在 DragonFly BSD 創始人 Matthew Dillon 實現的 slab 分配器基礎上,針對嵌入式系統優化的內存分配算法。最原始的 slab 算法是 Jeff Bonwick 為 Solaris 操作系統而引入的一種高效內核內存分配算法。RT-Thread 的 slab 分配器實現主要是去掉了其中的對象構造及析構過程,只保留了純粹的緩沖型的內存池算法。slab 分配器會根據對象的大小分成多個區(zone),也可以看成每類對象有一個內存池,如下圖所示:
slab 內存分配結構圖(來源RT-Thread編程指南)
一個 zone 的大小在 32K 到 128K 字節之間,分配器會在堆初始化時根據堆的大小自動調整。系統中的 zone 最多包括 72 種對象,一次最大能夠分配 16K 的內存空間,如果超出了 16K 那么直接從頁分配器中分配。每個 zone 上分配的內存塊大小是固定的,能夠分配相同大小內存塊的 zone 會鏈接在一個鏈表中,而 72 種對象的 zone 鏈表則放在一個數組(zone_array[])中統一管理。
下面是內存分配器主要的兩種操作:
(1)內存分配:假設分配一個 32 字節的內存,slab 內存分配器會先按照 32 字節的值,從 zone array 鏈表表頭數組中找到相應的 zone 鏈表。如果這個鏈表是空的,則向頁分配器分配一個新的 zone,然后從 zone 中返回第一個空閑內存塊。如果鏈表非空,則這個 zone 鏈表中的第一個 zone 節點必然有空閑塊存在(否則它就不應該放在這個鏈表中),那么就取相應的空閑塊。如果分配完成后,zone 中所有空閑內存塊都使用完畢,那么分配器需要把這個 zone 節點從鏈表中刪除。
(2)內存釋放:分配器需要找到內存塊所在的 zone 節點,然后把內存塊鏈接到 zone 的空閑內存塊鏈表中。如果此時zone 的空閑鏈表指示出 zone 的所有內存塊都已經釋放,即 zone 是完全空閑的,那么當 zone 鏈表中全空閑 zone 達到一定數目后,系統就會把這個全空閑的 zone 釋放到頁面分配器中去。
4、memheap 管理算法
(1)memheap 管理算法適用于系統含有多個地址可不連續的內存堆。使用 memheap 內存管理可以簡化系統存在多個內存堆時的使用:當系統中存在多個內存堆的時候,用戶只需要在系統初始化時將多個所需的memheap 初始化,并開啟 memheap 功能就可以很方便地把多個 memheap(地址可不連續)粘合起來用于系統的 heap 分配。
注意:在開啟 memheap 之后原來的 heap 功能將被關閉,兩者只可以通過打開或關閉RT_USING_MEMHEAP_AS_HEAP來選擇其一。
(2)memheap 工作機制如下圖所示,首先將多塊內存加入memheap_item鏈表進行粘合。當分配內存塊時,會先從默認內存堆去分配內存,當分配不到時會查找 memheap_item 鏈表,嘗試從其他的內存堆上分配內存塊。應用程序不用關心當前分配的內存塊位于哪個內存堆上,就像是在操作一個內存堆。
memheap 處理多內存堆(來源RT-Thread編程指南)
(3)對于有部分ST MCU是將內部SRAM分為地址不連續的兩部分SRAM1和SRAM2,那么就可以用memheap管理算法,例如IoT board的MCU STM32L475VET6。在前面講將到的 “ZI 段結尾處” 到內存尾部的空間用作內存堆,只是修改了SRAM1(96K)部分,那么如果想用SRAM2(32K)部分,需要修改代碼。
(A)在board.h中加入如下代碼:
1/*根據自己的MCU不同,確認MCU內部SRAM是否有分為兩塊SRAM1和SRAM2,STM32L475VET6內部SRAM分為SRAM1和SRAM2兩塊地址不連續*/2#defineSTM32_SRAM2_SIZE323#defineSTM32_SRAM2_BEGIN(0x10000000u)4#defineSTM32_SRAM2_END(0x10000000+STM32_SRAM2_SIZE*1024)5#defineSTM32_SRAM2_HEAP_SIZE((uint32_t)STM32_SRAM2_END-(uint32_t)STM32_SRAM2_BEGIN)
(B)在board.c中加入如下代碼:
1#ifdefined(RT_USING_MEMHEAP)&&defined(RT_USING_MEMHEAP_AS_HEAP)2staticstructrt_memheapsystem_heap;3#endif
(C)修改board.c中的rt_hw_board_init函數,內存堆配置和初始化代碼改為:
1#ifdefined(RT_USING_MEMHEAP)&&defined(RT_USING_MEMHEAP_AS_HEAP)2rt_system_heap_init((void*)HEAP_BEGIN,(void*)HEAP_END);3rt_memheap_init(&system_heap,"sram2",(void*)STM32_SRAM2_BEGIN,STM32_SRAM2_HEAP_SIZE);4#else5rt_system_heap_init((void*)HEAP_BEGIN,(void*)HEAP_END);6#endif
(4)根據自己是否想用使用SRAM2來決定是否使用memheap 管理算法,在rtconfig.h打開關閉相關宏來實現,如需要使用memheap 管理算法,打開如下宏:
1#defineRT_USING_MEMHEAP//定義該宏可開啟兩個或以上內存堆拼接的使用,未定義則關閉2#defineRT_USING_MEMHEAP_AS_HEAP
(5)如果RT_USING_MEMHEAP和RT_USING_MEMHEAP_AS_HEAP這兩個宏打開了,則使用memheap,那么系統內存堆的時候首先會從SRAM1(96K的那塊)分配內存,當SRAM1(96K的那塊)用完了再到SRAM2(32K那塊)分配。
(6)打開RT_USING_MEMHEAP_AS_HEAP之后,實現的算法不同,比如rt_malloc()函數的實現。
5、內存堆配置和初始化
(1)在使用內存堆時,必須要在系統初始化的時候進行堆的初始化,可以通過下面的函數接口完成:
1voidrt_system_heap_init(void*begin_addr,void*end_addr);
(A)入口參數:
begin_addr:堆內存區域起始地址。end_addr:堆內存區域結束地址。
(2)在使用 memheap 堆內存時,必須要在系統初始化的時候進行堆內存的初始化,可以通過下面的函數接口完成:
1rt_err_trt_memheap_init(structrt_memheap*memheap,2constchar*name,3void*start_addr,4rt_uint32_tsize);
(A)入口參數:
memheap:memheap 控制塊。name:內存堆的名稱。start_addr:堆內存區域起始地址。size:堆內存大小。
(B)返回值:
RT_EOK:成功。
6、內存堆的管理方式
(1)申請內存塊:會從系統堆空間中找到合適大小的內存塊,然后把內存塊可用地址返回給用戶,函數接口如下:
1void*rt_malloc(rt_size_tsize);
(A)入口參數:
size:需要分配的內存塊的大小,單位為字節。
(B)返回值:
分配的內存塊地址:成功。RT_NULL:失敗。
(2)釋放內存塊:應用程序使用完從內存分配器中申請的內存后,必須及時釋放,否則會造成內存泄漏,會把待釋放的內存還回給堆管理器中,函數接口如下:
1voidrt_free(void*rmem);
(A)入口參數:
rmem:待釋放的內存塊指針。
(3)重分配內存塊:在已分配內存塊的基礎上重新分配內存塊的大?。ㄔ黾踊蚩s小),在進行重新分配內存塊時,原來的內存塊數據保持不變(縮小的情況下,后面的數據被自動截斷),函數接口如下:
1void*rt_realloc(void*rmem,rt_size_tnewsize);
(A)入口參數:
rmem:指向已分配的內存塊。newsize:重新分配的內存大小。
(B)返回值:
重新分配的內存塊地址:成功。RT_NULL:失敗。
(4)分配多內存塊:從內存堆中分配連續內存地址的多個內存塊,可以通過下面的函數接口完成:
1void*rt_calloc(rt_size_tcount,rt_size_tsize);
(A)入口參數:
count:內存塊數量。size:內存塊容量。
(B)返回值:
指向第一個內存塊地址的指針:成功,并且所有分配的內存塊都被初始化成零。RT_NULL:分配失敗。
(5)設置分配內存鉤子函數:在分配內存塊過程中,用戶可設置一個鉤子函數,設置的鉤子函數會在內存分配完成后進行回調。回調時,會把分配到的內存塊地址和大小做為入口參數傳遞進去,函數接口如下:
1voidrt_malloc_sethook(void(*hook)(void*ptr,rt_size_tsize));
(A)hook:鉤子函數指針。
(B)void hook(void *ptr, rt_size_t size); 函數接口參數:
ptr:分配到的內存塊指針。 size:分配到的內存塊的大小。
(6)設置是否內存鉤子函數:在釋放內存時,用戶可設置一個鉤子函數,設置的鉤子函數會在調用內存釋放完成前進行回調?;卣{時,釋放的內存塊地址會做為入口參數傳遞進去(此時內存塊并沒有被釋放),函數接口如下:
1voidrt_free_sethook(void(*hook)(void*ptr));
(A)hook:鉤子函數指針。
(B)void hook(void *ptr); 函數接口參數:
ptr:待釋放的內存塊指針。
五、內存池
內存堆管理器可以分配任意大小的內存塊,非常靈活和方便。但其也存在明顯的缺點:一是分配效率不高,在每次分配時,都要空閑內存塊查找;二是容易產生內存碎片。為了提高內存分配的效率,并且避免內存碎片,RT-Thread 提供了另外一種內存管理方法:內存池(Memory Pool)。內存池(Memory Pool)是一種用于分配大量大小相同的小內存對象的技術。它可以極大加快內存分配/釋放的速度。
1、內存塊分配機制
(1)內存池在創建時先向系統申請一大塊內存,然后分成大小相等的多個小內存塊,小內存塊直接通過鏈表連接起來(此鏈表也稱為空閑內存鏈表)。每次分配的時候,從空閑內存鏈表中取出表頭上第一個內存塊,提供給申請者。物理內存中允許存在多個大小不同的內存池,每一個內存池又由多個大小相同的空閑內存塊組成。當一個內存池對象被創建時,內存池對象就被分配給了一個內存池控制塊,內存控制塊的參數包括內存池名,內存緩沖區,內存塊大小,塊數以及一個等待線程列表。
內存池示意圖(來源[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》)
(2)內核負責給內存池分配內存池控制塊,它同時也接收用戶線程的分配內存塊申請,當獲得這些信息后,內核就可以從內存池中為內存池分配內存。內存池一旦初始化完成,內部的內存塊大小將不能再做調整。
2、內存池的管理方式
(1)創建內存池:創建內存池操作將會創建一個內存池對象并從堆上分配一個內存池。創建內存池是從對應內存池中分配和釋放內存塊的先決條件,創建內存池后,線程便可以從內存池中執行申請、釋放等操作。函數接口如下:
1rt_mp_trt_mp_create(constchar*name,2rt_size_tblock_count,3rt_size_tblock_size);
(A)入口參數:name:內存池名。block_count:內存塊數量。block_size:內存塊容量。
(B)返回值:
內存池的句柄:創建內存池對象成功。RT_NULL:創建失敗。
(2)刪除內存池:將刪除內存池對象并釋放申請的內存,刪除內存池時,會首先喚醒等待在該內存池對象上的所有線程(返回 RT_ERROR),然后再釋放已從內存堆上分配的內存池數據存放區域,然后刪除內存池對象。函數接口如下:
1rt_err_trt_mp_delete(rt_mp_tmp);
(A)入口參數:mp:rt_mp_create返回的內存池對象句柄。
(B)返回值:RT_EOK:刪除成功。
(3)初始化內存池:初始化內存池跟創建內存池類似,只是初始化內存池用于靜態內存管理模式,內存池控制塊來源于用戶在系統中申請的靜態對象。另外與創建內存池不同的是,此處內存池對象所使用的內存空間是由用戶指定的一個緩沖區空間,用戶把緩沖區的指針傳遞給內存池控制塊,其余的初始化工作與創建內存池相同。函數接口如下:
1rt_err_trt_mp_init(structrt_mempool*mp,2constchar*name,3void*start,4rt_size_tsize,5rt_size_tblock_size);
(A)入口參數:
mp:內存池對象。name:內存池名。start:內存池的起始位置。size:內存池數據區域大小。block_size:內存塊容量。
(B)返回值:
RT_EOK:初始化成功。RT_ERROR:失敗。
注意:內存池塊個數 = size / (block_size + 4 鏈表指針大小),計算結果取整數。例如:內存池數據區總大小 size 設為 4096 字節,內存塊大小 block_size 設為 80 字節;則申請的內存塊個數為 4096/ (80+4)= 48 個。
(4)脫離內存池:脫離內存池將把內存池對象從內核對象管理器中脫離,內核先喚醒所有等待在該內存池對象上的線程,然后將內存池對象從內核對象管理器中脫離。函數接口如下:
1rt_err_trt_mp_detach(structrt_mempool*mp);
(A)入口參數:
mp:內存池對象。
(B)返回值:
RT_EOK:成功。
(5)分配內存塊:從指定的內存池中分配一個內存塊,函數接口如下:
1void*rt_mp_alloc(rt_mp_tmp,rt_int32_ttime);
(A)入口參數:
mp:內存池對象。
time:超時時間。如果內存池中有可用的內存塊,則從內存池的空閑塊鏈表上取下一個內存塊,減少空閑塊數目并返回這個內存塊;如果內存池中已經沒有空閑內存塊,則判斷超時時間設置:若超時時間設置為零,則立刻返回空內存塊;若等待時間大于零,則把當前線程掛起在該內存池對象上,直到內存池中有可用的自由內存塊,或等待時間到達。
(B)返回值:
分配的內存塊地址:成功。RT_NULL:失敗。
(6)釋放內存塊:任何內存塊使用完后都必須被釋放,否則會造成內存泄露。首先通過需要被釋放的內存塊指針計算出該內存塊所在的(或所屬于的)內存池對象,然后增加內存池對象的可用內存塊數目,并把該被釋放的內存塊加入空閑內存塊鏈表上。接著判斷該內存池對象上是否有掛起的線程,如果有,則喚醒掛起線程鏈表上的首線程。函數接口如下:
1voidrt_mp_free(void*block);
(A)入口參數:block:內存塊指針。
六、基于STM32的內存管理實驗
光說不練都是假把式,那么接下來就行內存管理的實際操作,基于STM32,使用RTT&正點原子聯合出品潘多拉開發板,實現兩個實驗,分別是內存堆管理實驗和內存池實驗。
1、內存堆管理實驗
(1)實現代碼:
1#include"main.h" 2#include"board.h" 3#include"rtthread.h" 4#include"data_typedef.h" 5#include"key.h" 6 7/*線程句柄*/ 8staticrt_thread_tthread1=RT_NULL; 9voiddynmem_sample(void);1011intmain(void)12{13dynmem_sample();14return0;15}1617/**************************************************************18函數名稱:thread1_entry19函數功能:線程1入口函數20輸入參數:parameter:入口參數21返回值:無22備注:無23**************************************************************/24voidthread1_entry(void*parameter)25{26u8key;27char*ptr=RT_NULL;2829while(1)30{31key=key_scan(0);3233if(key==KEY0_PRES)34{35ptr=rt_malloc(10);36if(ptr!=RT_NULL)37{38rt_kprintf("rt_mallocsuccessful\r\n");39sprintf(ptr,"%s","helloRTT");40rt_kprintf("0x%p\r\n",ptr);/*打印分配到的地址*/41rt_kprintf("%s\r\n",ptr);42}43else44{45rt_kprintf("rt_mallocfailed\r\n");46}4748rt_thread_mdelay(2000);4950if(ptr!=RT_NULL)51{52rt_free(ptr);53ptr=RT_NULL;54rt_kprintf("rt_freesuccessful\r\n");55}56else57{58rt_kprintf("rt_freefailed,ptr!=NULL\r\n");59}60}6162rt_thread_mdelay(1);63}64}656667voiddynmem_sample(void)68{69thread1=rt_thread_create("thread1",70thread1_entry,71NULL,72512,733,7420);75if(thread1!=RT_NULL)76{77rt_thread_startup(thread1);;78}79else80{81rt_kprintf("createthread1failed\r\n");82return;83}84}
(2)觀察FinSH,開機按下3次KEY0,如下現象,會打印出申請到內存的地址,2秒后釋放內存:
2、內存池實驗
(1)實現代碼:
1#include"main.h" 2#include"board.h" 3#include"rtthread.h" 4#include"data_typedef.h" 5#include"key.h" 6 7/*線程句柄*/ 8staticrt_thread_tthread2=RT_NULL; 9 10staticrt_mp_tmp; 11 12voidmempool_sample(void); 13 14intmain(void) 15{ 16mempool_sample(); 17 18return0; 19} 20 21/************************************************************** 22函數名稱:thread2_entry 23函數功能:線程2入口函數 24輸入參數:parameter:入口參數 25返回值:無 26備注:無 27**************************************************************/ 28voidthread2_entry(void*parameter) 29{ 30u8key; 31char*ptr=RT_NULL; 32 33while(1) 34{ 35key=key_scan(0); 36 37if(key==KEY1_PRES) 38{ 39ptr=rt_mp_alloc(mp,0); 40if(ptr!=RT_NULL) 41{ 42rt_kprintf("rt_mp_allocsuccessful\r\n"); 43sprintf(ptr,"%s","helloRTT"); 44rt_kprintf("0x%p\r\n",ptr);/*打印分配到的地址*/ 45rt_kprintf("%s\r\n",ptr); 46} 47else 48{ 49rt_kprintf("rt_mp_allocfailed\r\n"); 50} 51 52rt_thread_mdelay(2000); 53 54if(ptr!=RT_NULL) 55{ 56rt_mp_free(ptr); 57ptr=RT_NULL; 58rt_kprintf("rt_mp_freesuccessful\r\n"); 59} 60else 61{ 62rt_kprintf("rt_mp_freefailed,ptr!=NULL\r\n"); 63} 64} 65 66rt_thread_mdelay(1); 67} 68} 69 70 71voidmempool_sample(void) 72{ 73mp=rt_mp_create("mp1",20,20); 74 75if(mp!=RT_NULL) 76{ 77rt_kprintf("mempoolcreatesuccessful\r\n"); 78} 79else 80{ 81rt_kprintf("mempoolcreatefailed\r\n"); 82return; 83} 84 85thread2=rt_thread_create("thread2", 86thread2_entry, 87NULL, 88512, 893, 9020); 91if(thread2!=RT_NULL) 92{ 93rt_thread_startup(thread2);; 94} 95else 96{ 97rt_kprintf("createthread2failed\r\n"); 98return; 99}100101}
(2)觀察FinSH,開機,打印創建mempool成功信息,連續按3次KEY1,打印如下信息,包括申請到內存的地址,2秒后釋放內存:
參考文獻:
1、[野火?]《RT-Thread 內核實現與應用開發實戰—基于STM32》
2、《RT-THREAD 編程指南》
-
函數
+關注
關注
3文章
4327瀏覽量
62573 -
內存管理
+關注
關注
0文章
168瀏覽量
14134 -
RT-Thread
+關注
關注
31文章
1285瀏覽量
40085
原文標題:社區新人的RT-Thread學習筆記8——內存管理
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯網操作系統】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論