
今天分享一點關于RTOS內存管理方面的內容。
閱讀本文之前,建議先了解一下RTOS搶占式調度機制和時間片調度機制等相關內容。
uint8_t?buffer[128];
當你寫下這行代碼時,就意味著你使用了128字節的靜態內存,buffer的空間由編譯器靜態分配到棧中,且程序運行過程中,buffer的大小無法改變,這就稱之為靜態內存。
void?*buffer_ptr?=?NULL;
buffer_ptr?=?malloc(128?*?sizeof(uint8_t));
當你寫下這行代碼時,就意味著你使用了128字節的動態內存,buffer在程序運行時從堆中分配指定大小的空間,且不用的時候可以使用free釋放,歸還給堆空間。
靜態內存由編譯器分配,這個沒什么好說的~
一般情況下使用malloc申請分配動態內存有兩個缺陷:
① 由于分配算法的復雜度和堆空間的使用情況,分配的時間不定;
② 在不斷申請、釋放的過程中,容易因為內存對齊而產生碎片化內存;
這兩個缺陷在「實時」操作系統中是不允許的,所以操作系統必須提供一套有效、合理、時間可確定的動態內存管理機制。
既然傳統malloc存在兩個缺陷,那就抱著解決這兩個缺陷的目的出發,去建立一套更適合于嵌入式系統的動態內存管理系統。
目前有兩種不同的解決方案:動態內存堆管理算法(mmheap)和靜態內存池管理算法(mmblk),兩種方法各有優缺點,TencentOS-tiny中兩種管理算法都提供,接下來依托具體算法進行講述。
TLSF全稱Two-Level Segregated Fit memory allocator,兩級隔離Fit內存分配器,是一款通用的動態內存分配器,專門設計用于滿足實時要求。
?https://github.com/mattconte/tlsf
?
這款TLSF動態內存分配器具有以下特點:
TLSF主要采用兩級位圖(Two-Level Bitmap)與分級空閑塊鏈表(Segregated Free List)的數據結構管理動態內存池(memory pool)以及其中的空閑塊(free blocks),用Good-Fit的策略進行分配。
本文不對此算法進行深入講解(博主太菜~),如果感興趣可以查找TLSF算法論文閱讀,這里我只從應用角度給出一些需要注意的點:
①?「TLSF算法分配速度不一定快,只是說能保證分配的時間是個常數」(malloc不能保證);
② TLSF也叫多內存堆管理算法,「支持動態增加或者刪除多塊不連續的內存」,將它們作為一個內存堆使用;
TencentOS-tiny中此算法的實現的動態內存分配器在tos_mmheap.h和tos_mmheap.c中,其中默認指定最大可管理的不連續內存堆有三個,可以自行修改:
#define?K_MMHEAP_POOL_MAX?3
TencentOS-tiny中提供了默認的一個緩沖區作為堆空間,在tos_global.c中定義:
uint8_t?k_mmheap_default_pool[TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE]?__ALIGNED__(4);
大小在tos_config.h中指定:
#define?TOS_CFG_MMHEAP_EN???????????????1u
#define?TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE????????0x8000
/**
?*@brief???打印當前mmeheap使用情況
?*@param???none
?*@retval??none
*/
int?list_mmheap_info(void)
{
????k_err_t?err;
????k_mmheap_info_t?mmheap_info;
????err?=?tos_mmheap_check(&mmheap_info);
????if?(err?!=?K_ERR_NONE)?{
????????printf("current?mmheap?info?check?fail,?err?=?%d\r\n",?err);
????????return?-1;
????}
????else?{
????????printf("current?mmheap?info:\r\n\tused:?%d[0x%08x]?free:%d[0x%08x]\r\n\r\n",?mmheap_info.used,?mmheap_info.used,?mmheap_info.free,?mmheap_info.free);
????????return?0;
????}
}
void?task1_entry(void?*arg)
{???
????void?*ptr?=?NULL;
????size_t?size;
????
????/*?輸出塊大小的最大值?*/
????printf("K_MMHEAP_BLK_SIZE_MAX?is?%d(0x%08x)?bytes\r\n\r\n",?K_MMHEAP_BLK_SIZE_MAX,?K_MMHEAP_BLK_SIZE_MAX);
????
????/*?打印當前內存使用情況?*/
????list_mmheap_info();
????
????/*?申請一塊內存使用?*/
????size?=?128;
????ptr?=?tos_mmheap_alloc(size);
????if?(ptr?==?NULL)?{
????????printf("%d?bytes?mem?alloc?fail\r\n",?size);
????}
????else?{
????????printf("%d?bytes?mem?alloc?success,?ptr?is?0x%08x\r\n\r\n",?size,?(uint32_t)ptr);
????}
????
????/*?打印當前內存使用情況?*/
????list_mmheap_info();
????
????/*?釋放申請的內存?*/
????tos_mmheap_free(ptr);
????printf("mem?free?success\r\n\r\n");
????
????/*?打印當前內存使用情況?*/
????list_mmheap_info();
????
????while?(1)?{
????????tos_task_delay(1000);
????}
}
運行結果為:
靜態內存池就是將一塊內存劃分為n個大小相等的塊,用戶可以動態的申請、釋放一個塊,假裝在使用動態內存。
TencentOS-tiny中靜態內存池管理算法的實現在tos_mmblk.h和tos_mmblk.c中。
提供如下四個API:
//創建一個內存池
__API__?k_err_t?tos_mmblk_pool_create(k_mmblk_pool_t?*mbp,?void?*pool_start,?size_t?blk_num,?size_t?blk_size);
//銷毀一個內存池
__API__?k_err_t?tos_mmblk_pool_destroy(k_mmblk_pool_t?*mbp);
//申請內存池中的一個空閑塊
__API__?k_err_t?tos_mmblk_alloc(k_mmblk_pool_t?*mbp,?void?**blk);
//釋放回內存池一個內存塊
__API__?k_err_t?tos_mmblk_free(k_mmblk_pool_t?*mbp,?void?*blk);
typedef?struct?blk_st?{
????int???id;
????char*?payload;
}?blk_t;
#define?BLK_NUM?10
k_mmblk_pool_t?mmblk_pool;
uint8_t?mmblk_pool_buffer[BLK_NUM?*?sizeof(blk_t)];
void?task1_entry(void?*arg)
{???
????blk_t?*ptr?=?NULL;
????k_err_t?err;
????
????/*?打印出一個塊的大小?*/
????printf("block?size?is?%d?bytes\r\n",?sizeof(blk_t));
????
????/*?申請一個塊?*/
????err?=?tos_mmblk_alloc(&mmblk_pool,?(void*)&ptr);
????if?(err?!=?K_ERR_NONE)?{
????????printf("a?mmblk?alloc?fail,?err?=?%d\r\n",?err);
????????return;
????}
????else?{
????????printf("a?mmblk?alloc?success\r\n");
????}
????
????/*?使用該塊?*/
????ptr->id?=?1;
????ptr->payload?=?"hello";
????printf("mmblk?id:%d?payload:%s\r\n",?ptr->id,?ptr->payload);
????
????/*?使用完畢之后釋放?*/
????err?=?tos_mmblk_free(&mmblk_pool,?ptr);
????if?(err?!=?K_ERR_NONE)?{
????????printf("a?mmblk?free?fail,?err?=?%d\r\n",?err);
????????return;
????}
????else?{
????????printf("a?mmblk?free?success\r\n");
????}
????
????while?(1)?{
????????tos_task_delay(1000);
????}
}
運行結果如圖:
本節主要講述了使用malloc和free的缺點:申請時間未知,內存容易產生碎片。
所以在實時操作系統中誕生了動態堆管理機制和靜態內存池管理機制,兩種比較如下:
① 內存堆(mmheap)管理機制,每次可以申請和釋放「不定大小的內存」,分配時間雖然不快,但是能保證已知;
② 靜態內存池(mmblk)管理機制,每次只能申請和釋放一個塊(「固定大小的內存」),分配時間最快,沒有碎片。

1.嵌友們,BUG不好找?那是因為宏定義沒用好!
2.GD32V RISC-V MCU調試體驗
3.2022年六大值得關注的邊緣計算趨勢
4.2021年第12期《單片機與嵌入式系統應用》電子刊新鮮出爐!
5.STM32的SPI外設片選只有一個,怎么破?
6.C語言開發單片機,為什么都是全局變量形式?

免責聲明:本文系網絡轉載,版權歸原作者所有。如涉及作品版權問題,請與我們聯系,我們將根據您提供的版權證明材料確認版權并支付稿酬或者刪除內容。