RT-Thread內(nèi)核的我們已經(jīng)基本都學(xué)習(xí)過了,除了基本的線程操作和通信,
內(nèi)核部分還有內(nèi)存管理和中斷處理,本文主要就來說說內(nèi)存管理相關(guān)問題。
目錄
前言
一、為什么要內(nèi)存管理
二、RT-Thread 內(nèi)存堆管理
2.1 RT-Thread 內(nèi)存分配
2.2 RT-Thread內(nèi)存堆管理方式
2.2.1 內(nèi)存堆管理的3種方式
2.2.2 管理方式的程序配置
2.3 內(nèi)存堆 API 函數(shù)
三、RT-Thread 內(nèi)存池
3.1 內(nèi)存池的位置
3.2 內(nèi)存池程序配置和控制塊
3.3 內(nèi)存池操作 API 函數(shù)
結(jié)語
前言
記得最初學(xué)習(xí) RT-Thread ,對(duì)于內(nèi)存管理我也是簡單看看然后一筆帶過,當(dāng)時(shí)覺得用不上,在我做的一些傳感器單品項(xiàng)目上,對(duì)于內(nèi)存管理確實(shí)知道與不知道沒什么關(guān)系,但是隨著認(rèn)知的增長,項(xiàng)目復(fù)雜程度增加,發(fā)現(xiàn)內(nèi)存管理還不可或缺,于是今時(shí)今日正好再次來更新 RT-Thread記錄,有必要好好的說一說。
RT-Thread 有2種內(nèi)存管理方式,分別是動(dòng)態(tài)內(nèi)存堆管理和靜態(tài)內(nèi)存池管理。
先不管這兩種方式是怎么實(shí)現(xiàn)的,首先要明白一個(gè)問題,為什么要內(nèi)存管理?
本 RT-Thread 專欄記錄的開發(fā)環(huán)境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發(fā)環(huán)境 及 配合CubeMX開發(fā)快速上手)
RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程 — 啟動(dòng)文件和源碼分析)
RT-Thread 內(nèi)核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread 線程操作函數(shù)及線程管理與FreeRTOS的比較)
RT-Thread記錄(四、RT-Thread 時(shí)鐘節(jié)拍和軟件定時(shí)器)
RT-Thread記錄(五、RT-Thread 臨界區(qū)保護(hù))
RT-Thread記錄(六、IPC機(jī)制之信號(hào)量、互斥量和事件集)
RT-Thread記錄(七、IPC機(jī)制之郵箱、消息隊(duì)列)
一、為什么要內(nèi)存管理
什么是內(nèi)存管理?為什么需要內(nèi)存管理?
在我們的程序設(shè)計(jì)中,一些數(shù)據(jù)需要的內(nèi)存大小需要在程序運(yùn)行過程中根據(jù)實(shí)際情況確定,在用戶需要一段內(nèi)存空間時(shí),向系統(tǒng)申請(qǐng),系統(tǒng)選擇一段合適的內(nèi)存空間分配給用戶,用戶使用完畢后,再釋放回系統(tǒng),以便系統(tǒng)將該段內(nèi)存空間回收再利用。
這樣子不斷的申請(qǐng)釋放,如果沒有內(nèi)存管理,就會(huì)導(dǎo)致內(nèi)存碎片,對(duì)程序產(chǎn)生極大的影響。
具體的原因我在下面博文有詳細(xì)說明:淺談 malloc 函數(shù)在單片機(jī)上的應(yīng)用
如果看完上面的博文,就應(yīng)該知道了內(nèi)存管理的重要性。
我們確實(shí)直接在函數(shù)或者線程里面創(chuàng)建臨時(shí)變量使用 線程棧 或 者系統(tǒng)棧處理臨時(shí)變量,但是正如上面博文里面所說的,作為一個(gè)通用的操作系統(tǒng),都會(huì)且必須得有自己的合理的內(nèi)存管理方式。
這個(gè)在我們現(xiàn)在說明的 RT-Thread 操作系統(tǒng)上是內(nèi)核已經(jīng)實(shí)現(xiàn)好的,我們得了解它。
知道了為什么,那么接下來我們就來了解一下 RT-Thread 的這2種內(nèi)存管理方式。
二、RT-Thread 內(nèi)存堆管理
現(xiàn)在看看內(nèi)存堆管理, 所謂堆,看過我博文的朋友應(yīng)該已經(jīng)很熟悉了,對(duì)于裸機(jī)中的堆的位置,大小設(shè)定都應(yīng)該會(huì)有一個(gè)詳細(xì)的認(rèn)知,如果還不知道堆,可以查看下面這篇博文一步到位的理解:
STM32的內(nèi)存管理相關(guān)(內(nèi)存架構(gòu),內(nèi)存管理,map文件分析)
上文雖然是以STM32為例子說明,但是對(duì)于 C/C++ 程序編譯后的存儲(chǔ)數(shù)據(jù)段 在不同芯片上的順序基本都是一樣的,于是乎在我的又一篇博文中
就得到了一張內(nèi)存分配的示意圖:

2.1 RT-Thread 內(nèi)存分配
要理解 RT-Thread 內(nèi)存堆管理,首先得知道它管理的是哪一塊內(nèi)存,所以得知道 RT-Thread 內(nèi)存分配。
其實(shí)細(xì)心的朋友會(huì)發(fā)現(xiàn),我在前面文章《RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程 — 啟動(dòng)文件和源碼分析)》
中提到過 RT-Thread 內(nèi)存分配情況,在其中的 2.2.2 RT-Thread 堆和棧空間說明(與FreeRTOS不同)有過說明:


通過上面的分析,我們可以得到 RT-Thread 下的內(nèi)存分配的示意圖:

通過上面我們自己的分析,再結(jié)合官方說明文檔的說明,我們應(yīng)該能完全明白了什么是 RT-Thread 的內(nèi)存堆:

至此,我們可以確實(shí)的明白,RT-Thread 內(nèi)存管理管理的是哪一部分的內(nèi)存了。
2.2 RT-Thread內(nèi)存堆管理方式
那么RT-Thread 對(duì)于內(nèi)存堆,是如何管理的呢?
說明:對(duì)于這部分,我個(gè)人記錄是以知道為主,畢竟我們專欄以應(yīng)用為主,我們了解會(huì)用即可,如果以后確實(shí)自己會(huì)需要自己寫內(nèi)存管理,肯定會(huì)單獨(dú)更新一篇博文。
我們先前的基礎(chǔ)介紹直接使用官網(wǎng)說明,本節(jié)后部分會(huì)說明下程序中是怎么選擇使用的。
2.2.1 內(nèi)存堆管理的3種方式
RT-Thread 內(nèi)存堆管理又根據(jù)具體內(nèi)存設(shè)備劃分為三種情況:
- 針對(duì)小內(nèi)存塊的分配管理(小內(nèi)存管理算法)
- 針對(duì)大內(nèi)存塊的分配管理(slab 管理算法)
- 針對(duì)多內(nèi)存堆的分配情況(memheap 管理算法)
這里套用官方的介紹做個(gè)簡單說明(如果需要深入了解可以去官網(wǎng)查看):



2.2.2 管理方式的程序配置
我們上面 簡單介紹了RT-Thread 內(nèi)存堆管理的3種方式,雖然我們沒有詳細(xì)分析內(nèi)部是如何實(shí)現(xiàn),那么我們也得了解在 RT-Thread 工程中,是怎么配置使用哪一種管理方式,同時(shí)了解這些方式的實(shí)現(xiàn)在程序什么文件中。
在工程的rtconfig.h中有關(guān)于內(nèi)存管理方式的配置:

上圖是使用小內(nèi)存堆管理算法。
如果是使用 slab 管理算法,需要宏定義如下:
#define RT_USING_SLAB
#define RT_USING_HEAP
如果是使用 memheap 管理算法,需要宏定義如下:
#define RT_USING_MEMHEAP_AS_HEAP
具體的實(shí)現(xiàn)文件是在工程 mem.c 文件中,如下圖:

RT-Thread 內(nèi)存管理詳細(xì)的實(shí)現(xiàn)方式可以自行查看該文件,這里就不過多介紹。
2.3 內(nèi)存堆 API 函數(shù)
對(duì)于 RT-Thread 內(nèi)存堆管理,是有自己的 malloc 函數(shù),不能直接用 c 語言庫中原始的malloc 函數(shù)。
其 三種管理算法提供的 API 都是相同的。
初始化:
首先是初始化函數(shù):
/*小內(nèi)存堆和slab 管理算法*/
void rt_system_heap_init(void* begin_addr, void* end_addr);
/*memheap 管理算法*/
rt_err_t rt_memheap_init(struct rt_memheap *memheap,
const char *name,
void *start_addr,
rt_uint32_t size)
我在《RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程 — 啟動(dòng)文件和源碼分析)》板級(jí)硬件初始化 — rt_hw_board_init 小結(jié)中提到過 這個(gè)函數(shù):

RT-Thread內(nèi)存堆初始化函數(shù)是在系統(tǒng)啟動(dòng)時(shí)候初始化的,而不是我們用戶在main函數(shù)中再初始化的。
內(nèi)存管理操作函數(shù):
簡單記錄一下:
/*
分配內(nèi)存塊
參數(shù):
nbytes 需要分配的內(nèi)存塊的大小,單位為字節(jié)
返回 ——
分配的內(nèi)存塊地址 成功
RT_NULL 失敗
*/
void *rt_malloc(rt_size_t nbytes);
/*
釋放內(nèi)存塊
參數(shù):
ptr 待釋放的內(nèi)存塊指針
*/
void rt_free (void *ptr);
/*
重分配內(nèi)存塊
參數(shù) 描述
rmem 指向已分配的內(nèi)存塊
newsize 重新分配的內(nèi)存大小
返回 ——
重新分配的內(nèi)存塊地址 成功
*/
void *rt_realloc(void *rmem, rt_size_t newsize);
/*
分配多內(nèi)存塊
參數(shù) 描述
count 內(nèi)存塊數(shù)量
size 內(nèi)存塊容量
返回 ——
指向第一個(gè)內(nèi)存塊地址的指針 成功 ,并且所有分配的內(nèi)存塊都被初始化成零。
RT_NULL 分配失敗
*/
void *rt_calloc(rt_size_t count, rt_size_t size);
/*
設(shè)置內(nèi)存鉤子函數(shù)
參數(shù) 描述
hook 鉤子函數(shù)指針
*/
void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size));
/*
上面函數(shù)的hook 函數(shù)接口
參數(shù) 描述
ptr 分配到的內(nèi)存塊指針
size 分配到的內(nèi)存塊的大小
*/
void hook(void *ptr, rt_size_t size);
/*
釋放內(nèi)存鉤子函數(shù)
參數(shù) 描述
hook 鉤子函數(shù)指針
*/
void rt_free_sethook(void (*hook)(void *ptr));
/*
上面函數(shù)的hook 函數(shù)接口
參數(shù) 描述
ptr 待釋放的內(nèi)存塊指針
*/
void hook(void *ptr);

釋放內(nèi)存塊后要清空內(nèi)存塊指針,不然會(huì)成為野指針。
三、RT-Thread 內(nèi)存池
RT-Thread 的第二種內(nèi)存管理方式是 內(nèi)存池,內(nèi)存池是一種內(nèi)存分配方式,用于分配大量大小相同的小內(nèi)存塊,它可以極大地加快內(nèi)存分配與釋放的速度,且能盡量避免內(nèi)存碎片化。
他是為了提高內(nèi)存分配的效率,并且避免內(nèi)存碎片而產(chǎn)生的。
基本介紹套用官方說明:

內(nèi)存池屬于 內(nèi)核對(duì)象!!支持線程掛起功能。
內(nèi)核對(duì)線什么意思?就是我們前面說到的,線程,IPC機(jī)制,這些東西都是內(nèi)核對(duì)象,所以存在 內(nèi)存池 控制塊。
有一個(gè)點(diǎn)要注意,內(nèi)存池申請(qǐng)的內(nèi)存塊大小固定!
3.1 內(nèi)存池的位置
內(nèi)存池也是內(nèi)存管理,那么他創(chuàng)建的之后在內(nèi)存的什么位置呢?
首先明白內(nèi)存池作為一個(gè)對(duì)象,其實(shí)可以認(rèn)為是一個(gè)變量,那么他基本上就是屬于.bss段的東西了,在RT-Thread 中,他的位置應(yīng)該是在 .bss段的。
我們按照上面的內(nèi)存分配的示意圖說明一下內(nèi)存池申請(qǐng)的內(nèi)存位置:

為了驗(yàn)證一下是否如此,可以初始化一個(gè)內(nèi)存池,編譯后查看一下.map文件:

內(nèi)存池屬于.bss段的數(shù)據(jù),只不過他一般來說都是申請(qǐng)的相對(duì)比較大的一塊內(nèi)存空間,然后在這個(gè)大空間內(nèi)自己有自己的分配管理方式。
3.2 內(nèi)存池程序配置和控制塊
內(nèi)存池程序配置:
RT-Thread 的內(nèi)存池在程序中的配置和實(shí)現(xiàn)文件如下圖:

內(nèi)存池控制塊:(因?yàn)槭莾?nèi)對(duì)象,所以還是熟悉的配方,熟悉的味道~ ~)
struct rt_mempool
{
struct rt_object parent;
void *start_address; /* 內(nèi)存池?cái)?shù)據(jù)區(qū)域開始地址 */
rt_size_t size; /* 內(nèi)存池?cái)?shù)據(jù)區(qū)域大小 */
rt_size_t block_size; /* 內(nèi)存塊大小 */
rt_uint8_t *block_list; /* 內(nèi)存塊列表 */
/* 內(nèi)存池?cái)?shù)據(jù)區(qū)域中能夠容納的最大內(nèi)存塊數(shù) */
rt_size_t block_total_count;
/* 內(nèi)存池中空閑的內(nèi)存塊數(shù) */
rt_size_t block_free_count;
/* 因?yàn)閮?nèi)存塊不可用而掛起的線程列表 */
rt_list_t suspend_thread;
/* 因?yàn)閮?nèi)存塊不可用而掛起的線程數(shù) */
rt_size_t suspend_thread_count;
};
typedef struct rt_mempool* rt_mp_t;
內(nèi)存池具體管理方法的實(shí)現(xiàn)可以自己查看下源碼,這里暫時(shí)不做深入研究。
3.3 內(nèi)存池操作 API 函數(shù)
內(nèi)存池作為內(nèi)核對(duì)象,那么他的操作就和以前那些IPC機(jī)制,線程一樣的方式,分為動(dòng)態(tài)創(chuàng)建,靜態(tài)初始化這些。
并不需要在 板級(jí)初始化的時(shí)候就初始化。用戶可以自己選擇用于不用。
簡單記錄說明一下內(nèi)存池操作函數(shù):
/*
創(chuàng)建內(nèi)存池
參數(shù) 描述
name 內(nèi)存池名
block_count 內(nèi)存塊數(shù)量
block_size 內(nèi)存塊容量
返回 ——
內(nèi)存池的句柄 創(chuàng)建內(nèi)存池對(duì)象成功
RT_NULL 創(chuàng)建失敗
*/
rt_mp_t rt_mp_create(const char* name,
rt_size_t block_count,
rt_size_t block_size);
/*
刪除內(nèi)存池
參數(shù) 描述
mp rt_mp_create 返回的內(nèi)存池對(duì)象句柄
返回 ——
RT_EOK 刪除成功
*/
rt_err_t rt_mp_delete(rt_mp_t mp);
/*
初始化內(nèi)存池
參數(shù) 描述
mp 內(nèi)存池對(duì)象
name 內(nèi)存池名
start 內(nèi)存池的起始位置
size 內(nèi)存池?cái)?shù)據(jù)區(qū)域大小
block_size 內(nèi)存塊容量
返回 ——
RT_EOK 初始化成功
- RT_ERROR 失敗
*/
rt_err_t rt_mp_init(rt_mp_t mp,
const char* name,
void *start, rt_size_t size,
rt_size_t block_size);
/*
脫離內(nèi)存池
參數(shù) 描述
mp 內(nèi)存池對(duì)象
返回 ——
RT_EOK 成功
*/
rt_err_t rt_mp_detach(rt_mp_t mp);
/*
分配內(nèi)存塊
參數(shù) 描述
mp 內(nèi)存池對(duì)象
time 超時(shí)時(shí)間
返回 ——
分配的內(nèi)存塊地址 成功
RT_NULL 失敗
*/
void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time);
/*
釋放內(nèi)存塊
參數(shù) 描述
block 內(nèi)存塊指針
*/
void rt_mp_free (void *block);
結(jié)語
本文從為什么要內(nèi)存管理說起,了解了 RT-Thread 的2種內(nèi)存管理方式:內(nèi)存堆 和 內(nèi)存池。
相信不管你平時(shí)用不用動(dòng)態(tài)內(nèi)存申請(qǐng),看了這篇文章以后也會(huì)對(duì) RT-Thread 的內(nèi)存管理有一定的了解,在以后需要用到動(dòng)態(tài)內(nèi)存分配的同時(shí)也不會(huì)手足無措!
但是特別注意,不管使用哪種方式,在申請(qǐng)的內(nèi)存使用完之后都必須及時(shí)釋放,而且要注意清空相應(yīng)的指針!
但是本文的重點(diǎn)在于理解 為什么要內(nèi)存管理,和 內(nèi)存管理管理的是哪一塊的內(nèi)存,對(duì)于如何實(shí)現(xiàn)內(nèi)存管理我們只是借用了官方的說明,還是沒有深入的研究分析。這個(gè)需要等以后我有機(jī)會(huì)自己寫一套內(nèi)存管理方式的時(shí)候,或許會(huì)單獨(dú)開一篇文章深入分析。
謝謝!
審核編輯:湯梓紅
-
中斷處理
+關(guān)注
關(guān)注
0文章
96瀏覽量
11479 -
內(nèi)存管理
+關(guān)注
關(guān)注
0文章
171瀏覽量
14878 -
RT-Thread
+關(guān)注
關(guān)注
32文章
1613瀏覽量
44822
發(fā)布評(píng)論請(qǐng)先 登錄
RT-Thread記錄(一、版本開發(fā)環(huán)境及配合CubeMX)
RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程)
【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集
RT-Thread編程指南
RT-Thread全球技術(shù)大會(huì):RT-Thread測試用例集合案例
大佬帶你理解RT-Thread內(nèi)核并上手實(shí)踐
RT-Thread學(xué)習(xí)筆記 RT-Thread的架構(gòu)概述
RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南
基于RT-Thread Studio學(xué)習(xí)
RT-Thread記錄(八、理解RT-Thread內(nèi)存管理)
評(píng)論