吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

RT-Thread記錄(八、理解RT-Thread內存管理)

矜辰所致 ? 來源:矜辰所致 ? 作者:矜辰所致 ? 2022-06-23 10:11 ? 次閱讀
RT-Thread內核的我們已經基本都學習過了,除了基本的線程操作和通信,
內核部分還有內存管理和中斷處理,本文主要就來說說內存管理相關問題。

目錄

前言
一、為什么要內存管理
二、RT-Thread 內存堆管理
2.1 RT-Thread 內存分配
2.2 RT-Thread內存堆管理方式
2.2.1 內存堆管理的3種方式
2.2.2 管理方式的程序配置
2.3 內存堆 API 函數
三、RT-Thread 內存池
3.1 內存池的位置
3.2 內存池程序配置和控制塊
3.3 內存池操作 API 函數
結語

前言

記得最初學習 RT-Thread ,對于內存管理我也是簡單看看然后一筆帶過,當時覺得用不上,在我做的一些傳感器單品項目上,對于內存管理確實知道與不知道沒什么關系,但是隨著認知的增長,項目復雜程度增加,發現內存管理還不可或缺,于是今時今日正好再次來更新 RT-Thread記錄,有必要好好的說一說。


RT-Thread 有2種內存管理方式,分別是動態內存堆管理和靜態內存池管理。

先不管這兩種方式是怎么實現的,首先要明白一個問題,為什么要內存管理?

本 RT-Thread 專欄記錄的開發環境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發環境 及 配合CubeMX開發快速上手)
RT-Thread記錄(二、RT-Thread內核啟動流程 — 啟動文件和源碼分析)
RT-Thread 內核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread 線程操作函數及線程管理與FreeRTOS的比較)
RT-Thread記錄(四、RT-Thread 時鐘節拍和軟件定時器
RT-Thread記錄(五、RT-Thread 臨界區保護)
RT-Thread記錄(六、IPC機制之信號量、互斥量和事件集)
RT-Thread記錄(七、IPC機制之郵箱、消息隊列)

一、為什么要內存管理

什么是內存管理?為什么需要內存管理?

在我們的程序設計中,一些數據需要的內存大小需要在程序運行過程中根據實際情況確定,在用戶需要一段內存空間時,向系統申請,系統選擇一段合適的內存空間分配給用戶,用戶使用完畢后,再釋放回系統,以便系統將該段內存空間回收再利用。

這樣子不斷的申請釋放,如果沒有內存管理,就會導致內存碎片,對程序產生極大的影響。

具體的原因我在下面博文有詳細說明:淺談 malloc 函數在單片機上的應用

如果看完上面的博文,就應該知道了內存管理的重要性。

我們確實直接在函數或者線程里面創建臨時變量使用 線程棧 或 者系統棧處理臨時變量,但是正如上面博文里面所說的,作為一個通用的操作系統,都會且必須得有自己的合理的內存管理方式。
這個在我們現在說明的 RT-Thread 操作系統上是內核已經實現好的,我們得了解它。

知道了為什么,那么接下來我們就來了解一下 RT-Thread 的這2種內存管理方式。

二、RT-Thread 內存堆管理

現在看看內存堆管理, 所謂堆,看過我博文的朋友應該已經很熟悉了,對于裸機中的堆的位置,大小設定都應該會有一個詳細的認知,如果還不知道堆,可以查看下面這篇博文一步到位的理解:

STM32的內存管理相關(內存架構,內存管理,map文件分析)

上文雖然是以STM32為例子說明,但是對于 C/C++ 程序編譯后的存儲數據段 在不同芯片上的順序基本都是一樣的,于是乎在我的又一篇博文中

嵌入式RTOS的 任務棧 和 系統棧

就得到了一張內存分配的示意圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

2.1 RT-Thread 內存分配

要理解 RT-Thread 內存堆管理,首先得知道它管理的是哪一塊內存,所以得知道 RT-Thread 內存分配。

其實細心的朋友會發現,我在前面文章《RT-Thread記錄(二、RT-Thread內核啟動流程 — 啟動文件和源碼分析)》

中提到過 RT-Thread 內存分配情況,在其中的 2.2.2 RT-Thread 堆和棧空間說明(與FreeRTOS不同)有過說明:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

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

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16

通過上面我們自己的分析,再結合官方說明文檔的說明,我們應該能完全明白了什么是 RT-Thread 的內存堆:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16

至此,我們可以確實的明白,RT-Thread 內存管理管理的是哪一部分的內存了。

2.2 RT-Thread內存堆管理方式

那么RT-Thread 對于內存堆,是如何管理的呢?

說明:對于這部分,我個人記錄是以知道為主,畢竟我們專欄以應用為主,我們了解會用即可,如果以后確實自己會需要自己寫內存管理,肯定會單獨更新一篇博文。
我們先前的基礎介紹直接使用官網說明,本節后部分會說明下程序中是怎么選擇使用的。

2.2.1 內存堆管理的3種方式

RT-Thread 內存堆管理又根據具體內存設備劃分為三種情況:

  • 針對小內存塊的分配管理(小內存管理算法
  • 針對大內存塊的分配管理(slab 管理算法)
  • 針對多內存堆的分配情況(memheap 管理算法)

這里套用官方的介紹做個簡單說明(如果需要深入了解可以去官網查看):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

2.2.2 管理方式的程序配置

我們上面 簡單介紹了RT-Thread 內存堆管理的3種方式,雖然我們沒有詳細分析內部是如何實現,那么我們也得了解在 RT-Thread 工程中,是怎么配置使用哪一種管理方式,同時了解這些方式的實現在程序什么文件中。

在工程的rtconfig.h中有關于內存管理方式的配置:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16

上圖是使用小內存堆管理算法。

如果是使用 slab 管理算法,需要宏定義如下:

#define RT_USING_SLAB
#define RT_USING_HEAP

如果是使用 memheap 管理算法,需要宏定義如下:

#define RT_USING_MEMHEAP_AS_HEAP

具體的實現文件是在工程 mem.c 文件中,如下圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

RT-Thread 內存管理詳細的實現方式可以自行查看該文件,這里就不過多介紹。

2.3 內存堆 API 函數

對于 RT-Thread 內存堆管理,是有自己的 malloc 函數,不能直接用 c 語言庫中原始的malloc 函數。

其 三種管理算法提供的 API 都是相同的。

初始化:

首先是初始化函數:

/*小內存堆和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內核啟動流程 — 啟動文件和源碼分析)》板級硬件初始化 — rt_hw_board_init 小結中提到過 這個函數:

poYBAGKzy-OAQ_syAAEYWgSIaJ0335.png

RT-Thread內存堆初始化函數是在系統啟動時候初始化的,而不是我們用戶在main函數中再初始化的。

內存管理操作函數:

簡單記錄一下:

/*
分配內存塊
參數:
nbytes 	需要分配的內存塊的大小,單位為字節
返回 	——
分配的內存塊地址 	成功
RT_NULL 	失敗
*/
void *rt_malloc(rt_size_t nbytes);
/*
釋放內存塊
參數:
ptr 	待釋放的內存塊指針
*/
void rt_free (void *ptr);
/*
重分配內存塊
參數 	描述
rmem 	指向已分配的內存塊
newsize 	重新分配的內存大小
返回 	——
重新分配的內存塊地址 	成功
*/
void *rt_realloc(void *rmem, rt_size_t newsize);
/*
分配多內存塊
參數 	描述
count 	內存塊數量
size 	內存塊容量
返回 	——
指向第一個內存塊地址的指針 	成功 ,并且所有分配的內存塊都被初始化成零。
RT_NULL 	分配失敗
*/
void *rt_calloc(rt_size_t count, rt_size_t size);

/*
設置內存鉤子函數
參數 	描述
hook 	鉤子函數指針
*/
void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size));
/*
上面函數的hook 函數接口
參數 	描述
ptr 	分配到的內存塊指針
size 	分配到的內存塊的大小
*/
void hook(void *ptr, rt_size_t size);

/*
釋放內存鉤子函數
參數 	描述
hook 	鉤子函數指針
*/
void rt_free_sethook(void (*hook)(void *ptr));
/*
上面函數的hook 函數接口
參數 	描述
ptr 	待釋放的內存塊指針
*/
void hook(void *ptr);

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16

釋放內存塊后要清空內存塊指針,不然會成為野指針。

三、RT-Thread 內存池

RT-Thread 的第二種內存管理方式是 內存池,內存池是一種內存分配方式,用于分配大量大小相同的小內存塊,它可以極大地加快內存分配與釋放的速度,且能盡量避免內存碎片化。
他是為了提高內存分配的效率,并且避免內存碎片而產生的。

基本介紹套用官方說明:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

內存池屬于 內核對象!!支持線程掛起功能。

內核對線什么意思?就是我們前面說到的,線程,IPC機制,這些東西都是內核對象,所以存在 內存池 控制塊。

有一個點要注意,內存池申請的內存塊大小固定!

3.1 內存池的位置

內存池也是內存管理,那么他創建的之后在內存的什么位置呢?

首先明白內存池作為一個對象,其實可以認為是一個變量,那么他基本上就是屬于.bss段的東西了,在RT-Thread 中,他的位置應該是在 .bss段的。

我們按照上面的內存分配的示意圖說明一下內存池申請的內存位置:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16

為了驗證一下是否如此,可以初始化一個內存池,編譯后查看一下.map文件:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

內存池屬于.bss段的數據,只不過他一般來說都是申請的相對比較大的一塊內存空間,然后在這個大空間內自己有自己的分配管理方式。

3.2 內存池程序配置和控制塊

內存池程序配置:

RT-Thread 的內存池在程序中的配置和實現文件如下圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

內存池控制塊:(因為是內對象,所以還是熟悉的配方,熟悉的味道~ ~)

struct rt_mempool
{
    struct rt_object parent;

    void        *start_address;  /* 內存池數據區域開始地址 */
    rt_size_t     size;           /* 內存池數據區域大小 */

    rt_size_t     block_size;    /* 內存塊大小  */
    rt_uint8_t    *block_list;   /* 內存塊列表  */

    /* 內存池數據區域中能夠容納的最大內存塊數  */
    rt_size_t     block_total_count;
    /* 內存池中空閑的內存塊數  */
    rt_size_t     block_free_count;
    /* 因為內存塊不可用而掛起的線程列表 */
    rt_list_t     suspend_thread;
    /* 因為內存塊不可用而掛起的線程數 */
    rt_size_t     suspend_thread_count;
};
typedef struct rt_mempool* rt_mp_t;

內存池具體管理方法的實現可以自己查看下源碼,這里暫時不做深入研究。

3.3 內存池操作 API 函數

內存池作為內核對象,那么他的操作就和以前那些IPC機制,線程一樣的方式,分為動態創建,靜態初始化這些。

并不需要在 板級初始化的時候就初始化。用戶可以自己選擇用于不用。

簡單記錄說明一下內存池操作函數:

/*
創建內存池
參數 	描述
name 	內存池名
block_count 	內存塊數量
block_size 	內存塊容量
返回 	——
內存池的句柄 	創建內存池對象成功
RT_NULL 	創建失敗
*/
rt_mp_t rt_mp_create(const char* name,
                         rt_size_t block_count,
                         rt_size_t block_size);
/*
刪除內存池
參數 	描述
mp 	rt_mp_create 返回的內存池對象句柄
返回 	——
RT_EOK 	刪除成功
*/
rt_err_t rt_mp_delete(rt_mp_t mp);
/*
初始化內存池
參數 	描述
mp 	內存池對象
name 	內存池名
start 	內存池的起始位置
size 	內存池數據區域大小
block_size 	內存塊容量
返回 	——
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);
/*
脫離內存池
參數 	描述
mp 	內存池對象
返回 	——
RT_EOK 	成功
*/
rt_err_t rt_mp_detach(rt_mp_t mp);
/*
分配內存塊
參數 	描述
mp 	內存池對象
time 	超時時間
返回 	——
分配的內存塊地址 	成功
RT_NULL 	失敗
*/
void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time);
/*
釋放內存塊
參數 	描述
block 	內存塊指針
*/
void rt_mp_free (void *block);

結語

本文從為什么要內存管理說起,了解了 RT-Thread 的2種內存管理方式:內存堆 和 內存池。

相信不管你平時用不用動態內存申請,看了這篇文章以后也會對 RT-Thread 的內存管理有一定的了解,在以后需要用到動態內存分配的同時也不會手足無措!

但是特別注意,不管使用哪種方式,在申請的內存使用完之后都必須及時釋放,而且要注意清空相應的指針!

但是本文的重點在于理解 為什么要內存管理,和 內存管理管理的是哪一塊的內存,對于如何實現內存管理我們只是借用了官方的說明,還是沒有深入的研究分析。這個需要等以后我有機會自己寫一套內存管理方式的時候,或許會單獨開一篇文章深入分析。

謝謝!

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 中斷處理
    +關注

    關注

    0

    文章

    94

    瀏覽量

    11015
  • 內存管理
    +關注

    關注

    0

    文章

    168

    瀏覽量

    14188
  • RT-Thread
    +關注

    關注

    31

    文章

    1305

    瀏覽量

    40386
收藏 人收藏

    評論

    相關推薦

    RT-Thread記錄(一、版本開發環境及配合CubeMX)

    RT-Thread 學習記錄的第一篇文章,RT-Thread記錄(一、RT-Thread 版本、RT-T
    的頭像 發表于 06-20 00:28 ?5334次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(一、版本開發環境及配合CubeMX)

    RT-Thread記錄(二、RT-Thread內核啟動流程)

    在前面我們RT-Thread Studio工程基礎之上講一講RT-Thread內核啟動流程.
    的頭像 發表于 06-20 00:30 ?5096次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(二、<b class='flag-5'>RT-Thread</b>內核啟動流程)

    【原創精選】RT-Thread征文精選技術文章合集

    理解RT-Thread內存管理RT-Thread記錄(九、RTT中斷處理與階段小結)
    發表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開發組(2015-03-31)。RT-Thread做為國內有較大影響力的開源實時操作系統,本文是RT-Thread實時操作系統的編程指南
    發表于 11-26 16:06 ?0次下載

    RT-Thread全球技術大會:RT-Thread測試用例集合案例

    RT-Thread全球技術大會:RT-Thread測試用例集合案例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 16:34 ?2142次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:<b class='flag-5'>RT-Thread</b>測試用例集合案例

    大佬帶你理解RT-Thread內核并上手實踐

    RT-Thread內核的相關概念和基礎知識,然后了解RT-Thread系統的啟動流程、內存分布情況以及內核的配置方法。內核處于硬件層之上,包括了內核庫和實時內核的實現,而實時內核又包括:對象
    發表于 06-30 17:10 ?1316次閱讀

    RT-Thread學習筆記 RT-Thread的架構概述

    RT-Thread 簡介 作為一名 RTOS 的初學者,也許你對 RT-Thread 還比較陌生。然而,隨著你的深入接觸,你會逐漸發現 RT-Thread 的魅力和它相較于其他同類型 RTOS
    的頭像 發表于 07-09 11:27 ?4610次閱讀
    <b class='flag-5'>RT-Thread</b>學習筆記 <b class='flag-5'>RT-Thread</b>的架構概述

    RT-Thread文檔_RT-Thread 簡介

    RT-Thread文檔_RT-Thread 簡介
    發表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡介

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南
    發表于 02-22 18:23 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 潘多拉 STM32L475 上手指南

    RT-Thread文檔_線程管理

    RT-Thread文檔_線程管理
    發表于 02-22 18:28 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_線程<b class='flag-5'>管理</b>

    RT-Thread文檔_時鐘管理

    RT-Thread文檔_時鐘管理
    發表于 02-22 18:28 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_時鐘<b class='flag-5'>管理</b>

    RT-Thread文檔_內存管理

    RT-Thread文檔_內存管理
    發表于 02-22 18:30 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>內存</b><b class='flag-5'>管理</b>

    RT-Thread文檔_中斷管理

    RT-Thread文檔_中斷管理
    發表于 02-22 18:30 ?1次下載
    <b class='flag-5'>RT-Thread</b>文檔_中斷<b class='flag-5'>管理</b>

    RT-Thread文檔_RT-Thread SMP 介紹與移植

    RT-Thread文檔_RT-Thread SMP 介紹與移植
    發表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    基于RT-Thread Studio學習

    前期準備:從官網下載 RT-Thread Studio,弄個賬號登陸,開啟rt-thread學習之旅。
    的頭像 發表于 05-15 11:00 ?4123次閱讀
    基于<b class='flag-5'>RT-Thread</b> Studio學習
    百家乐注册就送| 百家乐筹码币套装| 大发888官方中文网址| 在线老虎机| 百家乐官网桌蓝盾在线| 百家乐官网破解赌戏玩| 百家乐官网园有限公司| 赌场百家乐视频| 百家乐预约| 棋牌室| 苏尼特右旗| 老k百家乐官网的玩法技巧和规则| 百家乐代理在线游戏可信吗网上哪家平台信誉好安全 | 蓝盾百家乐的玩法技巧和规则| 大发888ber娱乐场下载| 大发足球| 百家乐官网金海岸软件| 百家乐官网作弊| 百家乐过滤| 网络百家乐| 百家乐官网游戏开户网址| 威尼斯人娱乐城是真的吗| 百家乐官网新台第二局| 大哥大百家乐官网的玩法技巧和规则| 南京百家乐赌博现场被抓| 大发888娱乐城3403| 百家乐官网天下第一和| 百家乐百乐发破解版| 大发888网页版| 百家乐官网游戏资料网| 百家乐娱乐天上人间| 皇冠百家乐官网的玩法技巧和规则| 打百家乐纯打庄的方法| 大发888 m摩卡游戏| 百家乐官网打立了| 互博百家乐的玩法技巧和规则| 百家乐官网真钱游戏下载| 24山风水四大局| 神娱乐百家乐的玩法技巧和规则| 澳门百家乐官网有限公司| 狮威百家乐赌场娱乐网规则|