鸿蒙Hi3861学习十一-Huawei LiteOS-M(内存池)

一、简介

        LiteOS将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的。

        LiteOS内存管理模块管理系统的内存资源,包括:初始化分配释放

        不采用C标准库中的内存管理函数malloc和free的原因如下:

  1. 小型嵌入式设备的RAM不足,导致这些函数在有些情况下无法使用
  2. 内存管理的函数实现代码量可能非常大,占据了相当大的一块代码空间。
  3. 不安全,执行时间不确定。
  4. 容易产生碎片。这两个函数会使得连接器配置变得非常复杂。

        内存池是线程安全的固定大小的内存块。它的操作速度比动态分配的堆要快得多,而且不会受到碎片的影响。由于是线程安全的,所以,可以从中断中访问

        内存池可以看做是一个固定大小,且大小相同内存块的链表从池中分配内存,只是从列表中解除块链,并将控制权交给用户。释放内存到池,只是将块重新链到空闲链表中

         共享内存是线程间交换信息的基本模型之一。与使用消息队列相比,使用内存池交换数据可以在线程之间共享更复杂的对象

Memory Pool

二、特点

        内存管理模块通过对内存的释放、申请操作,来管理用户和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:任务IDosThreadNew的返回值

        返回值:

        任务栈大小

        实例:

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。即已完全释放掉。

猜你喜欢

转载自blog.csdn.net/qq_26226375/article/details/130582374