一、简介
LiteOS将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的。
LiteOS内存管理模块管理系统的内存资源,包括:初始化、分配、释放。
不采用C标准库中的内存管理函数malloc和free的原因如下:
- 小型嵌入式设备的RAM不足,导致这些函数在有些情况下无法使用
- 内存管理的函数实现代码量可能非常大,占据了相当大的一块代码空间。
- 不安全,执行时间不确定。
- 容易产生碎片。这两个函数会使得连接器配置变得非常复杂。
内存池是线程安全的固定大小的内存块。它的操作速度比动态分配的堆要快得多,而且不会受到碎片的影响。由于是线程安全的,所以,可以从中断中访问。
内存池可以看做是一个固定大小,且大小相同的内存块的链表。从池中分配内存,只是从列表中解除块链,并将控制权交给用户。释放内存到池,只是将块重新链到空闲链表中。
共享内存是线程间交换信息的基本模型之一。与使用消息队列相比,使用内存池交换数据可以在线程之间共享更复杂的对象。
二、特点
内存管理模块通过对内存的释放、申请操作,来管理用户和OS对内存的使用。使内存的利用率和使用率达到最优,同时最大限度地解决系统的内存碎片问题。
内存管理分为:静态内存管理和动态内存管理。
静态内存管理:在静态内存池中分配用户初始化时预设(固定)大小的内存块
- 优点:分配和释放效率高,静态内存池中无碎片
- 缺点:只能申请到初始化预设的内存块,不能按需申请。
动态内存:在动态内存池中分配用户指定大小的内存块
- 优点:按需分配
- 缺点:内存池可能会存在碎片
注:小熊派目前使用的都是动态内存的方式。
三、API介绍
osMemoryPoolNew
函数功能:
创建内存池。不能在中断中调用。
函数原型:
osMemoryPoolId_t osMemoryPoolNew (uint32_t block_count, uint32_t block_size, const osMemoryPoolAttr_t *attr);
参数:
block_count:申请的内存块个数
block_size:每个内存块的大小
attr:属性。自定义内存时使用,默认为NULL
返回值:
NULL:失败
其他值:内存池ID
实例:
osMemoryPoolId_t mpid_MemPool = NULL;
mpid_MemPool = osMemoryPoolNew(MEMPOOL_OBJECTS,sizeof(MEM_BLOCK_t),NULL);
osMemoryPoolAlloc
函数功能:
从内存池中申请内存块。如果内存池空,则挂起,直到有内存块可用。如果超时时间为0,可在中断中调用。
函数原型:
void *osMemoryPoolAlloc (osMemoryPoolId_t mp_id, uint32_t timeout);
参数:
mp_id:内存池ID。创建内存池osMemoryPoolNew时获得。
timeout:等待超时时间
返回值:
获取到的内存块地址
实例:
MEM_BLOCK_t *pMem = NULL;
osMemoryPoolId_t mpid_MemPool = NULL;
pMem = osMemoryPoolAlloc(mpid_MemPool,100);
osMemoryPoolFree
函数功能:
释放内存块到内存池中。可在中断中调用。
函数原型:
osStatus_t osMemoryPoolFree (osMemoryPoolId_t mp_id, void *block);
参数:
mp_id:内存池ID。创建内存池osMemoryPoolNew时获得。
block:要释放的内存块地址。osMemoryPoolAlloc的返回值。
返回值:
osOK:成功、
其他值:失败
实例:
osMemoryPoolId_t mpid_MemPool = NULL;
MEM_BLOCK_t *pMem = NULL;
osStatus_t rst = osMemoryPoolFree(mpid_MemPool,pMem);
osMemoryPoolDelete
函数功能:
删除内存池。不能在中断中使用。
函数原型:
osStatus_t osMemoryPoolDelete (osMemoryPoolId_t mp_id);
参数:
mp_id:内存池ID。创建内存池osMemoryPoolNew时获得。
返回值:
osOK:成功、
其他值:失败
实例:
osMemoryPoolId_t mpid_MemPool = NULL;
osStatus_t ret = osMemoryPoolDelete(mpid_MemPool);
LOS_MemAlloc
函数功能:
内存申请
函数原型:
VOID *LOS_MemAlloc(VOID *pool, UINT32 size)
参数:
pool:内存池地址。OS_SYS_MEM_ADDR为系统内存池。
size:要申请的大小。
返回值:
NULL:失败
其他值:申请到的内存地址
实例:
void *test_memroy = NULL;
test_memroy = LOS_MemAlloc(OS_SYS_MEM_ADDR, 1024);
if(test_memroy == NULL){} //失败
LOS_MemFree
函数功能:
内存释放
函数原型:
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
参数:
pool:内存池地址。OS_SYS_MEM_ADDR 为系统内存池
ptr:释放的内存地址。
返回值:
0:成功
1:失败
实例:
void *test_memroy = NULL;
if(LOS_MemFree(OS_SYS_MEM_ADDR,test_memroy) == 0){} //成功
osThreadGetStackSize
函数功能:
获取任务栈大小
函数原型:
uint32_t osThreadGetStackSize(osThreadId_t thread_id)
参数:
thread_id:任务ID,osThreadNew的返回值
返回值:
任务栈大小
实例:
osThreadGetStackSize(osThreadGetId());
osThreadGetStackSpace
函数功能:
获取任务空闲栈大小
函数原型:
uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
参数:
thread_id:任务ID,osThreadNew的返回值
返回值:
空闲栈大小
实例:
osThreadGetStackSpace(osThreadGetId());
LOS_MemPoolSizeGet
函数功能:
获取内存池大小
函数原型:
UINT32 LOS_MemPoolSizeGet(const VOID *pool)
参数:
pool: 内存池地址,OS_SYS_MEM_ADDR 为系统内存池
返回值:
内存池大小
实例:
LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR);
LOS_MemTotalUsedGet
函数功能:
获取已使用的内存大小
函数原型:
UINT32 LOS_MemTotalUsedGet(VOID *pool)
参数:
pool: 内存池地址,OS_SYS_MEM_ADDR 为系统内存池
返回值:
已使用的内存大小
实例:
LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR);
四、实例
创建两个任务,创建一个内存池,一个任务申请内存池内存,一个任务释放。
#include <stdio.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#define LOG_I(fmt, args...) printf("<%8ld> - [MEMORY]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...) printf("<%8ld>-[MEMORY_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);
osMemoryPoolId_t mpid_MemPool = NULL;
#define MEMPOOL_OBJECTS 1 // number of Memory Pool Objects
typedef struct { // object data type
char Buf[32];
} MEM_BLOCK_t;
MEM_BLOCK_t *pMem = NULL;
uint32_t g_index = 0;
void Thread_Memory1(void *argument)
{
(void)argument;
mpid_MemPool = osMemoryPoolNew(MEMPOOL_OBJECTS,sizeof(MEM_BLOCK_t),NULL);
if(mpid_MemPool == NULL)
{
LOG_E("memory pool create fail");
return;
}
while(1)
{
pMem = osMemoryPoolAlloc(mpid_MemPool,100);
if(pMem == NULL)
{
LOG_E("memory pool alloc fail");
}
else
{
LOG_I("memory pool alloc success");
sprintf(pMem->Buf,"memory alloc cnt:%d",g_index++);
osThreadYield();
}
}
}
void Thread_Memory2(void *argument)
{
(void)argument;
while(1)
{
if(pMem != NULL)
{
LOG_I("read memory pool data is {%s}",pMem->Buf);
osStatus_t rst = osMemoryPoolFree(mpid_MemPool,pMem);
if(rst == osOK)
{
LOG_I("memory pool free success");
pMem = NULL;
osDelay(200);
if(g_index > 10)
{
osStatus_t ret = osMemoryPoolDelete(mpid_MemPool);
if(ret == osOK)
{
LOG_I("memory pool delete success");
}
else
{
LOG_E("memory pool delete fail-[%d]",ret);
}
}
}
else
{
LOG_E("memory pool free fail-[%d]",rst);
}
}
}
}
void app_memory_init(void)
{
osThreadAttr_t attr;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 10;
attr.priority = 25;
attr.name = "Thread_Memory1";
if (osThreadNew(Thread_Memory1, NULL, &attr) == NULL)
{
LOG_E("Falied to create Thread_Memory1!\n");
}
attr.name = "Thread_Memory2";
if (osThreadNew(Thread_Memory2, NULL, &attr) == NULL)
{
LOG_E("Falied to create Thread_Memory2!\n");
}
}
注:使用的是V1.1.0版本,编译无法通过,提示找不到osMemoryPoolXX的函数。这里就不管他了,反正感觉这种方式也不好用,所以又写了如下的代码。
创建两个任务,一个任务申请内存,一个任务释放内存。两个任务都实时打印内存变化。使用的是系统内存池。
#include <stdio.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "los_memory.h"
#define LOG_I(fmt, args...) printf("<%8ld> - [MEMORY]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...) printf("<%8ld>-[MEMORY_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);
static void *test_memroy = NULL;
void Thread_Memory1(void *argument)
{
(void)argument;
osThreadId_t temp_id = osThreadGetId();
const char *temp_name = osThreadGetName(temp_id);
while(1)
{
LOG_I("[%s]:osThreadGetStackSize:[%d],osThreadGetStackSpace:[%d],LOS_MemPoolSizeGet:[%d],LOS_MemTotalUsedGet:[%d],LOS_MemLeftSize:[%d]\r\n",
temp_name,osThreadGetStackSize(osThreadGetId()),osThreadGetStackSpace(osThreadGetId()),LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR),LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR),(LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR) - LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR)));
test_memroy = LOS_MemAlloc(OS_SYS_MEM_ADDR, 1024);
if(test_memroy == NULL)
{
LOG_E("memory malloc fail");
}
else
{
LOG_I("malloc address:0x%.8x",test_memroy);
LOG_I("[%s]:malloc success osThreadGetStackSize:[%d],osThreadGetStackSpace:[%d],LOS_MemPoolSizeGet:[%d],LOS_MemTotalUsedGet:[%d],LOS_MemLeftSize:[%d]\r\n",
temp_name,osThreadGetStackSize(osThreadGetId()),osThreadGetStackSpace(osThreadGetId()),LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR),LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR),(LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR) - LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR)));
}
osDelay(100);
}
}
void Thread_Memory2(void *argument)
{
(void)argument;
osThreadId_t temp_id = osThreadGetId();
const char *temp_name = osThreadGetName(temp_id);
while(1)
{
if(test_memroy != NULL)
{
LOG_I("[%s]:osThreadGetStackSize:[%d],osThreadGetStackSpace:[%d],LOS_MemPoolSizeGet:[%d],LOS_MemTotalUsedGet:[%d],LOS_MemLeftSize:[%d]\r\n",
temp_name,osThreadGetStackSize(osThreadGetId()),osThreadGetStackSpace(osThreadGetId()),LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR),LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR),(LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR) - LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR)));
if(LOS_MemFree(OS_SYS_MEM_ADDR,test_memroy) == 0)
{
LOG_I("free suceess");
test_memroy = NULL;
}
LOG_I("free address:0x%.8x",test_memroy);
LOG_I("[%s]:free success osThreadGetStackSize:[%d],osThreadGetStackSpace:[%d],LOS_MemPoolSizeGet:[%d],LOS_MemTotalUsedGet:[%d],LOS_MemLeftSize:[%d]\r\n",
temp_name,osThreadGetStackSize(osThreadGetId()),osThreadGetStackSpace(osThreadGetId()),LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR),LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR),(LOS_MemPoolSizeGet(OS_SYS_MEM_ADDR) - LOS_MemTotalUsedGet(OS_SYS_MEM_ADDR)));
}
osDelay(50);
}
}
void app_memory_init(void)
{
osThreadAttr_t attr;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 10;
attr.priority = 25;
attr.name = "Thread_Memory1";
if (osThreadNew(Thread_Memory1, NULL, &attr) == NULL)
{
LOG_E("Falied to create Thread_Memory1!\n");
}
attr.name = "Thread_Memory2";
attr.stack_size = 1024 * 5;
if (osThreadNew(Thread_Memory2, NULL, &attr) == NULL)
{
LOG_E("Falied to create Thread_Memory2!\n");
}
}
任务一的栈大小为1024*10,任务二的栈大小为1024*5。任务1申请系统内存池,任务2释放系统内存池。注意观察打印信息。
结果显示,任务1 的栈大小为10240=1024*10 ,任务2的栈大小而5120=1024*5。
1.任务1在申请内存前打印任务池大小。
2.申请成功,申请到的内存地址为0x000eaefc。
3.任务1打印申请后的内存池大小。对比1可以发现,在申请前,系统内存池剩余为116368,申请后内存池剩余为115328,相差1040。而程序申请的为1024,多了16字节。因为这16字节是用来记录内存存储相关信息的。这里不做过多介绍。
4.任务2打印释放前的相关信息
5.释放成功
6.释放后地址为0
7.任务2打印释放后的相关信息。可以看到,释放前系统内存池剩余115328,释放后系统内存池剩余116368,相差1040。即已完全释放掉。