您好,登錄后才能下訂單哦!
這篇文章主要講解了“LiteOS內(nèi)存管理方法是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“LiteOS內(nèi)存管理方法是什么”吧!
在系統(tǒng)運(yùn)行的過程中,一些內(nèi)存空間大小是不確定的,比如一些數(shù)據(jù)緩沖區(qū),所以系統(tǒng)需要提供內(nèi)存空間的管理能力,用戶可以在使用的時候申請需要的內(nèi)存空間,使用完畢釋放該空間,以便再次利用。
Huawei LiteOS 的內(nèi)存管理模塊通過對內(nèi)存的申請/釋放操作,來管理用戶和OS對內(nèi)存的使用,使內(nèi)存的利用率和使用效率達(dá)到最優(yōu),同時最大限度地解決系統(tǒng)的內(nèi)存碎片問題。
動態(tài)內(nèi)存管理,即在內(nèi)存資源充足的情況下,從系統(tǒng)配置的一塊比較大的連續(xù)內(nèi)存(內(nèi)存池),根據(jù)用戶需求,分配任意大小的內(nèi)存塊。當(dāng)用戶不需要該內(nèi)存塊時,又可以釋放回系統(tǒng)供下一次使用。
與靜態(tài)內(nèi)存相比,動態(tài)內(nèi)存管理的好處是按需分配,缺點是內(nèi)存池中容易出現(xiàn)碎片。
LiteOS動態(tài)內(nèi)存支持 DLINK 和 BEST LITTLE 兩種標(biāo)準(zhǔn)算法。
DLINK動態(tài)內(nèi)存管理結(jié)構(gòu)如下圖所示:
第一部分
堆內(nèi)存(也稱內(nèi)存池)的起始地址及堆區(qū)域總大小。
第二部分
本身是一個數(shù)組,每個元素是一個雙向鏈表,所有free節(jié)點的控制頭都會被分類掛在這個數(shù)組的雙向鏈表中。
第三部分
占用內(nèi)存池極大部分的空間,是用于存放各節(jié)點的實際區(qū)域。
LiteOS 的動態(tài)內(nèi)存分配支持最佳適配算法,即 BEST LITTLE,每次分配時選擇內(nèi)存池中最小最適合的內(nèi)存塊進(jìn)行分配。
LiteOS 動態(tài)內(nèi)存管理在最佳適配算法的基礎(chǔ)上加入了 SLAB 機(jī)制,用于分配固定大小的內(nèi)存塊,進(jìn)而減小產(chǎn)生內(nèi)存碎片的可能性。
LiteOS 內(nèi)存管理中的 SLAB 機(jī)制支持可配置的 SLAB CLASS 數(shù)目及每個 CLASS 的最大空間。
現(xiàn)以 SLAB CLASS 數(shù)目為 4,每個 CLASS 的最大空間為 512 字節(jié)為例說明 SLAB 機(jī)制:
在內(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)存管理機(jī)制初始化 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)存池按照最佳適配算法申請。需要注意的是,如果當(dāng)前的 SLAB CLASS 中無可用 SLAB 塊了,則直接向內(nèi)存池申請,而不會繼續(xù)向有著更大 SLAB 塊空間的 SLAB CLASS 申請。
釋放內(nèi)存時,先檢查釋放的內(nèi)存塊是否屬于 SLAB CLASS,如果是 SLAB CLASS 的內(nèi)存塊,則還回對應(yīng)的 SLAB CLASS 中,否則還回內(nèi)存池中。
LiteOS動態(tài)內(nèi)存管理的方法使用宏定義的方法使能,在用戶工程目錄下的OS_CONFIG
中的target_config.h
文件中配置。
在該文件中,找到下面這兩項宏定義,置為 YES 則表示使能:
開啟BEST LITTLE 算法
#define LOSCFG_MEMORY_BESTFIT YES
開啟SLAB機(jī)制
#define LOSCFG_KERNEL_MEM_SLAB YES
內(nèi)存管理的主要工作是動態(tài)的劃分并管理用戶分配好的內(nèi)存區(qū)間。
動態(tài)內(nèi)存管理主要是在用戶需要使用大小不等的內(nèi)存塊的場景中使用。當(dāng)用戶需要分配內(nèi)存時,可以通過操作系統(tǒng)的動態(tài)內(nèi)存申請函數(shù)索取指定大小內(nèi)存塊,一旦使用完畢,通過動態(tài)內(nèi)存釋放函數(shù)歸還所占用內(nèi)存,使之可以重復(fù)使用。
Huawei LiteOS 系統(tǒng)中的內(nèi)存管理模塊管理系統(tǒng)的內(nèi)存資源,主要提供內(nèi)存的初始化、分配以及釋放功能。
Huawei LiteOS 系統(tǒng)中提供的內(nèi)存管理 API 都是以 LOS 開頭,但是這些 API 使用起來比較復(fù)雜,所以本文中我們使用 Huawei IoT Link SDK 提供的統(tǒng)一API接口進(jìn)行實驗,這些接口底層已經(jīng)使用 LiteOS 提供的API實現(xiàn),對用戶而言更為簡潔,API列表如下:
osal的api接口聲明在<osal.h>中,使用相關(guān)的接口需要包含該頭文件,關(guān)于函數(shù)的詳細(xì)參數(shù)請參考該頭文件的聲明。
相關(guān)的接口定義在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。
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 |
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)存空間的指針 |
返回值 | 無返回值 |
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 |
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 |
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 |
本實驗中將創(chuàng)建一個任務(wù),從最小字節(jié)開始,不停的申請分配內(nèi)存,釋放分配的內(nèi)存,直到申請失敗,串口終端中觀察可以申請到的最大字節(jié)。
首先打開上一篇使用的 HelloWorld 工程,基于此工程進(jìn)行實驗。
在Demo文件夾右擊,新建文件夾osal_kernel_demo
用于存放內(nèi)核的實驗文件(如果已有請忽略這一步)。
接下來在此文件夾中新建一個實驗文件 osal_mem_demo.c
,開始編寫代碼:
/* 使用osal接口需要包含該頭文件 */ #include <osal.h> /* 任務(wù)入口函數(shù) */ static int mem_access_task_entry() { uint32_t i = 0; //循環(huán)變量 size_t mem_size; //申請的內(nèi)存塊大小 uint8_t* mem_ptr = NULL; //內(nèi)存塊指針 while (1) { /* 每次循環(huán)將申請內(nèi)存的大小擴(kuò)大一倍 */ mem_size = 1 << i++; /* 嘗試申請分配內(nèi)存 */ mem_ptr = osal_malloc(mem_size); /* 判斷是否申請成功 */ if(mem_ptr != NULL) { /* 申請成功,打印信息 */ printf("access %d bytes memory success!\r\n", mem_size); /* 釋放申請的內(nèi)存,便于下次申請 */ osal_free(mem_ptr); /* 將內(nèi)存塊指針置為NULL,避免稱為野指針 */ mem_ptr = NULL; printf("free memory success!\r\n"); } else { /* 申請失敗,打印信息,任務(wù)結(jié)束 */ printf("access %d bytes memory failed!\r\n", mem_size); return 0; } } } /* 標(biāo)準(zhǔn)demo啟動函數(shù),函數(shù)名不要修改,否則會影響下一步實驗 */ int standard_app_demo_main() { /* 創(chuàng)建任務(wù),任務(wù)優(yōu)先級為11,shell任務(wù)的優(yōu)先級為10 */ osal_task_create("mem_access_task",mem_access_task_entry,NULL,0x400,NULL,11); return 0; }
編寫完成之后,要將我們編寫的 osal_mem_demo.c文件添加到makefile中,加入整個工程的編譯:
這里有個較為簡單的方法,直接修改Demo文件夾下的user_demo.mk配置文件,添加如下代碼:
#example for osal_mem_demo ifeq ($(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中進(jìn)行編譯。
那么,如何配置 CONFIG_USER_DEMO 宏定義呢?在工程根目錄下的.sdkconfig
文件中的末尾即可配置:
因為我們修改了mk配置文件,所以點擊重新編譯按鈕進(jìn)行編譯,編譯完成后點擊下載按鈕燒錄程序。
程序燒錄之后,即可看到程序已經(jīng)開始運(yùn)行,在串口終端中可看到實驗的輸出內(nèi)容:
linkmain:V1.2.1 AT 11:30:59 ON Nov 28 2019 WELCOME TO IOT_LINK SHELL LiteOS:/>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!
可以看到,系統(tǒng)啟動后,首先打印版本號,串口shell的優(yōu)先級為10,最先打印shell信息,接下來內(nèi)存申請任務(wù)創(chuàng)建開始執(zhí)行,在該芯片上最大能申請的空間為 16384
字節(jié)。
感謝各位的閱讀,以上就是“LiteOS內(nèi)存管理方法是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對LiteOS內(nèi)存管理方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。