1. LiteOS內(nèi)核的內(nèi)存管理
1.1. 內(nèi)存管理
在系統(tǒng)運行的過程中,一些內(nèi)存空間大小是不確定的,比如一些數(shù)據(jù)緩沖區(qū),所以系統(tǒng)需要提供內(nèi)存空間的管理能力,用戶可以在使用的時候申請需要的內(nèi)存空間,使用完畢釋放該空間,以便再次利用。
Huawei LiteOS 的內(nèi)存管理模塊通過對內(nèi)存的申請/釋放操作,來管理用戶和OS對內(nèi)存的使用,使內(nèi)存的利用率和使用效率達到最優(yōu),同時最大限度地解決系統(tǒng)的內(nèi)存碎片問題。
1.2. 動態(tài)內(nèi)存管理
動態(tài)內(nèi)存管理,即在內(nèi)存資源充足的情況下,從系統(tǒng)配置的一塊比較大的連續(xù)內(nèi)存(內(nèi)存池),根據(jù)用戶需求,分配任意大小的內(nèi)存塊。當用戶不需要該內(nèi)存塊時,又可以釋放回系統(tǒng)供下一次使用。
與靜態(tài)內(nèi)存相比,動態(tài)內(nèi)存管理的好處是按需分配,缺點是內(nèi)存池中容易出現(xiàn)碎片。
LiteOS動態(tài)內(nèi)存支持 DLINK 和 BEST LITTLE 兩種標準算法。
1.2.1. DLINK 動態(tài)內(nèi)存管理算法
DLINK動態(tài)內(nèi)存管理結(jié)構如下圖所示:
第一部分
堆內(nèi)存(也稱內(nèi)存池)的起始地址及堆區(qū)域總大小。
第二部分
本身是一個數(shù)組,每個元素是一個雙向鏈表,所有free節(jié)點的控制頭都會被分類掛在這個數(shù)組的雙向鏈表中。
第三部分
占用內(nèi)存池極大部分的空間,是用于存放各節(jié)點的實際區(qū)域。
1.2.2. BEST LITTLE 算法(重點)
LiteOS 的動態(tài)內(nèi)存分配支持最佳適配算法,即 BEST LITTLE,每次分配時選擇內(nèi)存池中最小最適合的內(nèi)存塊進行分配。
LiteOS 動態(tài)內(nèi)存管理在最佳適配算法的基礎上加入了 SLAB 機制,用于分配固定大小的內(nèi)存塊,進而減小產(chǎn)生內(nèi)存碎片的可能性。
LiteOS 內(nèi)存管理中的 SLAB 機制支持可配置的 SLAB CLASS 數(shù)目及每個 CLASS 的最大空間。
現(xiàn)以 SLAB CLASS 數(shù)目為 4,每個 CLASS 的最大空間為 512 字節(jié)為例說明 SLAB 機制:
在內(nèi)存池中共有 4 個 SLAB CLASS,每個 SLAB CLASS 的總共可分配大小為 512 字節(jié),第一個 SLAB CLASS 被分為 32 個16 字節(jié)的 SLAB 塊,第二個 SLAB CLASS 被分為 16 個 3 2字節(jié)的 SLAB 塊,第三個 SLAB CLASS 被分為 8 個 64 字節(jié)的 SLAB 塊,第四個 SLAB CLASS 被分為 4 個 128 字節(jié)的 SLAB 塊。這 4 個 SLAB CLASS 是從內(nèi)存池中按照最佳適配算法分配出來的。
初始化內(nèi)存管理時,首先初始化內(nèi)存池,然后在初始化后的內(nèi)存池中按照最佳適配算法申請 4 個 SLAB CLASS,再逐個按照 SLAB 內(nèi)存管理機制初始化 4 個 SLAB CLASS。
每次申請內(nèi)存時,先在滿足申請大小的最佳 SLAB CLASS 中申請,(比如用戶申請 20 字節(jié)內(nèi)存,就在 SLAB 塊大小為 32 字節(jié)的 SLAB CLASS 中申請),如果申請成功,就將 SLAB 內(nèi)存塊整塊返回給用戶,釋放時整塊回收。如果滿足條件的 SLAB CLASS 中已無可以分配的內(nèi)存塊,則繼續(xù)向內(nèi)存池按照最佳適配算法申請。需要注意的是,如果當前的 SLAB CLASS 中無可用 SLAB 塊了,則直接向內(nèi)存池申請,而不會繼續(xù)向有著更大 SLAB 塊空間的 SLAB CLASS 申請。
釋放內(nèi)存時,先檢查釋放的內(nèi)存塊是否屬于 SLAB CLASS,如果是 SLAB CLASS 的內(nèi)存塊,則還回對應的 SLAB CLASS 中,否則還回內(nèi)存池中。
1.2.3. 兩種動態(tài)內(nèi)存管理方法的選擇
LiteOS動態(tài)內(nèi)存管理的方法使用宏定義的方法使能,在用戶工程目錄下的OS_CONFIG中的target_config.h文件中配置。
在該文件中,找到下面這兩項宏定義,置為 YES 則表示使能:
開啟BEST LITTLE 算法
#define?LOSCFG_MEMORY_BESTFIT?YES
開啟SLAB機制
#define?LOSCFG_KERNEL_MEM_SLAB?YES
1.3. 動態(tài)內(nèi)存管理的應用場景
內(nèi)存管理的主要工作是動態(tài)的劃分并管理用戶分配好的內(nèi)存區(qū)間。
動態(tài)內(nèi)存管理主要是在用戶需要使用大小不等的內(nèi)存塊的場景中使用。當用戶需要分配內(nèi)存時,可以通過操作系統(tǒng)的動態(tài)內(nèi)存申請函數(shù)索取指定大小內(nèi)存塊,一旦使用完畢,通過動態(tài)內(nèi)存釋放函數(shù)歸還所占用內(nèi)存,使之可以重復使用。
2. 動態(tài)內(nèi)存管理API
Huawei LiteOS 系統(tǒng)中的內(nèi)存管理模塊管理系統(tǒng)的內(nèi)存資源,主要提供內(nèi)存的初始化、分配以及釋放功能。
Huawei LiteOS 系統(tǒng)中提供的內(nèi)存管理 API 都是以 LOS 開頭,但是這些 API 使用起來比較復雜,所以本文中我們使用 Huawei IoT Link SDK 提供的統(tǒng)一API接口進行實驗,這些接口底層已經(jīng)使用 LiteOS 提供的API實現(xiàn),對用戶而言更為簡潔,API列表如下:
osal的api接口聲明在
相關的接口定義在osal.c中,基于LiteOS的接口實現(xiàn)在 liteos_imp.c文件中:
接口名 | 功能描述 |
---|---|
osal_malloc | 按字節(jié)申請分配動態(tài)內(nèi)存空間 |
osal_free | 釋放已經(jīng)分配的動態(tài)內(nèi)存空間 |
osal_zalloc | 按字節(jié)申請分配動態(tài)內(nèi)存空間,分配成功則初始化這塊內(nèi)存所有值為0 |
osal_realloc | 重新申請分配動態(tài)內(nèi)存空間 |
osal_calloc | 申請分配num個長度為size的動態(tài)內(nèi)存空間 |
無論選擇使用哪種動態(tài)內(nèi)存管理算法,都使用該API。
2.1. osal_malloc
osal_malloc接口用于按字節(jié)申請分配動態(tài)內(nèi)存空間,其接口原型如下:
void?*osal_malloc(size_t?size){????void?*ret?=?NULL;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->malloc)) ????{ ????????ret?=?s_os_cb->ops->malloc(size); ????}????return?ret; }
該接口的參數(shù)說明如下表:
參數(shù) | 描述 |
---|---|
size | 申請分配的內(nèi)存大小,單位Byte |
返回值 | 分配成功 - 返回內(nèi)存塊指針 |
? | 分配失敗 - 返回NULL |
?
2.2. osal_free
osal_free接口用于釋放已經(jīng)分配的動態(tài)內(nèi)存空間,其接口原型如下:
void??osal_free(void?*addr){????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->free)) ????{ ????????s_os_cb->ops->free(addr); ????}????return; }
內(nèi)存塊free之后,記得使內(nèi)存塊指針為NULL,否則會成為野指針!
該接口的參數(shù)說明如下表:
參數(shù) | 描述 |
---|---|
addr | 動態(tài)分配內(nèi)存空間的指針 |
返回值 | 無返回值 |
?
2.3. osal_zalloc
osal_zalloc接口用于按字節(jié)申請分配動態(tài)內(nèi)存空間,分配成功則初始化這塊內(nèi)存所有值為0,其接口原型如下:
void?*osal_zalloc(size_t?size){????void?*ret?=?NULL;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->malloc)) ????{ ????????ret?=?s_os_cb->ops->malloc(size);????????if(NULL?!=?ret) ????????{????????????memset(ret,0,size); ????????} ????}????return?ret; }
該接口的參數(shù)說明如下表:
參數(shù) | 描述 |
---|---|
size | 申請分配的內(nèi)存大小,單位Byte |
返回值 | 分配成功 - 返回內(nèi)存塊指針 |
? | 分配失敗 - 返回NULL |
?
2.4. osal_realloc
osal_realloc接口用于重新申請分配動態(tài)內(nèi)存空間,其接口原型如下:
void?*osal_realloc(void?*ptr,size_t?newsize){????void?*ret?=?NULL;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->realloc)) ????{ ????????ret?=?s_os_cb->ops->realloc(ptr,newsize); ????}????return?ret; }
該接口的參數(shù)說明如下表:
參數(shù) | 描述 |
---|---|
ptr | 已經(jīng)分配了內(nèi)存空間的指針 |
newsize | 申請分配的新的內(nèi)存大小,單位Byte |
返回值 | 分配成功 - 返回內(nèi)存塊指針 |
? | 分配失敗 - 返回NULL |
?
2.5. osal_calloc
osal_calloc接口用于申請分配num個長度為size的動態(tài)內(nèi)存空間,其接口原型如下:
void?*osal_calloc(size_t?n,?size_t?size){????void?*p?=?osal_malloc(n?*?size);????if(NULL?!=?p) ????{????????memset(p,?0,?n?*?size); ????}????return?p; }
該接口的參數(shù)說明如下表:
參數(shù) | 描述 |
---|---|
n | 申請分配內(nèi)存塊的數(shù)目 |
size | 申請分配的每個內(nèi)存塊的內(nèi)存大小,單位Byte |
返回值 | 分配成功 - 返回內(nèi)存塊指針 |
? | 分配失敗 - 返回NULL |
?
3. 動手實驗 —— 測試動態(tài)內(nèi)存分配的最大字節(jié)
實驗內(nèi)容
本實驗中將創(chuàng)建一個任務,從最小字節(jié)開始,不停的申請分配內(nèi)存,釋放分配的內(nèi)存,直到申請失敗,串口終端中觀察可以申請到的最大字節(jié)。
實驗代碼
首先打開上一篇使用的 HelloWorld 工程,基于此工程進行實驗。
在Demo文件夾右擊,新建文件夾osal_kernel_demo用于存放內(nèi)核的實驗文件(如果已有請忽略這一步)。
接下來在此文件夾中新建一個實驗文件?osal_mem_demo.c,開始編寫代碼:
/*?使用osal接口需要包含該頭文件?*/#include?
編寫完成之后,要將我們編寫的 osal_mem_demo.c文件添加到makefile中,加入整個工程的編譯:
這里有個較為簡單的方法,直接修改Demo文件夾下的user_demo.mk配置文件,添加如下代碼:
#example?for?osal_mem_demoifeq?($(CONFIG_USER_DEMO),?"osal_mem_demo")???? ????user_demo_src??=?${wildcard?$(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_mem_demo.c} endif
添加位置如圖:
這段代碼的意思是:
如果 CONFIG_USER_DEMO 宏定義的值是osal_mem_demo,則將osal_mem_demo.c文件加入到makefile中進行編譯。
那么,如何配置 CONFIG_USER_DEMO 宏定義呢?在工程根目錄下的.sdkconfig文件中的末尾即可配置:
因為我們修改了mk配置文件,所以點擊重新編譯按鈕進行編譯,編譯完成后點擊下載按鈕燒錄程序。
實驗現(xiàn)象
程序燒錄之后,即可看到程序已經(jīng)開始運行,在串口終端中可看到實驗的輸出內(nèi)容:
linkmain:V1.2.1?AT?11:30:59?ON?Nov?28?2019 WELCOME?TO?IOT_LINK?SHELLLiteOS:/>access?1?bytes?memory?success! free?memory?success! access?2?bytes?memory?success! free?memory?success! access?4?bytes?memory?success! free?memory?success! access?8?bytes?memory?success! free?memory?success! access?16?bytes?memory?success! free?memory?success! access?32?bytes?memory?success! free?memory?success! access?64?bytes?memory?success! free?memory?success! access?128?bytes?memory?success! free?memory?success! access?256?bytes?memory?success! free?memory?success! access?512?bytes?memory?success! free?memory?success! access?1024?bytes?memory?success! free?memory?success! access?2048?bytes?memory?success! free?memory?success! access?4096?bytes?memory?success! free?memory?success! access?8192?bytes?memory?success! free?memory?success! access?16384?bytes?memory?success! free?memory?success! access?32768?bytes?memory?failed!
評論