LiteOS 内存管理

参考:

  1. 【野火】物联网操作系统 LiteOS 开发实战指南
  2. Huawei LiteOS | 中文网

8. 内存管理

8.1 基本概念

8.1.1 概念

  • LiteOS操作系统将内核内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的,所以在 LiteOS 中提供了多种内存分配算法(分配策略),但是上层接口(API)却是统一的
  • LiteOS内存管理模块管理用于系统的内存资源,包括:
    • 初始化
    • 分配
    • 释放
  • 不采用C标准库中的内存管理函数malloc()free() 原因:
    • 小型嵌入式设备的RAM的不足,导致这些函数有些情况无法使用
    • 他们的实现可能非常大,占据了相当大的一块代码空间
    • 不安全,执行时间不确定
    • 容易产生碎片,这两个函数会使得连接器配置变得非常复杂

8.1.2 特点

  • 内存管理模块通过对内存的释放、申请操作,来管理用户和OS对内存的使用,使内存的利用率和使用率达到最优,同时最大限度地解决系统的内存碎片问题
  • 内存管理分为:静态内存管理动态内存管理
    • 静态内存管理:在静态内存池中分配用户初始化时预设(固定)大小的内存块
      • 优点:分配和释放效率高,静态内存池中无碎片
      • 缺点:智能申请到初始化预设的内存块,不能按需申请
    • 动态内存:在动态内存池中分配用户指定大小的内存块
      • 优点:按需分配
      • 内存池可能存在碎片
  • LiteOS给提供了多种内存动态匹配算法默认使用BestFit (最佳适应算法),其他算法还包括DLINK算法
    • BestFit算法:每次为作业分配内存时,总是把能满足要求,又是最小的空闲分区分配给作业,避免“大材小用”

8.2 应用场景

  • 内存管理的主要工作:动态划分并管理用户分配好的内存空间,主要是在用户需要使用大小不等的内存块的使用场景
  • 当用户需要分配内存时,可以通过操作系统的内存申请函数索取指定大小,一旦使用完毕,通过动态内存的释放函数归还占用内存,使之可以重复使用

8.3 内存运作机制

8.3.1 动态内存运作机制(BestFit算法)

动态内存管理,即在内存资源充足的情况下,从系统配置一块比较大的连续内存(内存池,其大小为OS_SYS_MEM_SIZE),根据用户的需求,分配任意大小的内存块,当用户不需要该内存块时,又可以释放回系统供下一次使用。

LiteOS 动态内存管理在最佳适配算法的基础上加入了 SLAB 机制,用于分配固定大小的内存块,进而减小产生内存碎片的可能性。

LiteOS 内存管理_第1张图片

  • 初始化内存,调用 LOS_MemInit
    • 首先初始化一个内存池
    • 在初始化后的内存池中生成一个内存信息管理节点LOS_HEAP_MANAGER),如上图第一部分
    • 申请 n 个 SLAB CLASS,再逐个按照 SLAB 内存管理机制初始化 n 个 SLAB CLASS,每个SLAB CLASS都有一个LOS_HEAP_NODE进行管理
  • 申请内存,调用 LOS_MemAlloc
    • 每次申请内存时,先在满足申请大小的最佳 SLAB CLASS 中申请
    • 申请成功,将SLAB内存块整块返回给用户,释放时整块回收
    • 如果满足条件的SLAB CLASS 中已无可以分配(最佳的,而不是更大的)的内存块,则继续向内存池按照最佳适配算法申请
  • 释放内存,调用 LOS_MemFree
    • 释放内存时,先检查释放的内存块是否属于SLAB CLASS,如果是SLAB CLASS的内存块,则归还对应的SLAB CLASS,否则归还内存池中

8.3.2 静态内存运作机制

  • 静态内存实质上是一块静态数组,静态内存池内的块大小在初始化时设定,初始化后块大小不可变更

  • 静态内存池由一个控制块和若干个相同大小的内存块构成。控制块位于内存池头部,用于内存块管理。

  • 内存块的申请和释放以块大小为粒度
    LiteOS 内存管理_第2张图片

8.4 开发说明

8.4.1 静态内存使用

注意:使用时需要包含“los_membox.h

  • 使用场景:当用户需要使用固定长度的内存时,可以使用静态内存分配的方式获取内存

  • 功能函数

    Huawei LiteOS的静态内存管理主要为用户提供以下功能。

    功能分类 接口名 描述
    初始化静态内存 LOS_MemboxInit 初始化一个静态内存池,设定其起始地址、总大小及每个块大小
    清除静态内存内容 LOS_MemboxClr 清零静态内存块
    申请一块静态内存 LOS_MemboxAlloc 申请一块静态内存块
    释放内存 LOS_MemboxFree 释放一个静态内存块
    分析静态内存池状态 LOS_MemboxStatisticsGet 获取静态内存池的统计信息
  • 开发流程

    • 开发一片内存区域作为静态内存池
    • 调用LOS_MemboxInit()接口,进行静态内存使用前的初始化,将入参指定的内存区域分割为N块(N值取决于静态内存总大小和块大小),将所有内存块挂到空闲链表,在内存起始处防止控制头
    • 调用LOS_MemboxAlloc()接口,系统将会从空闲链表中获取第一个空闲块,并返回该块的用户空间地址
    • 调用LOS_MemboxFree()接口,将该块内存加入空闲块链表
    • 调用LOS_MemboxClr()接口,系统内部清零静态内存块,将入参地址对应的内存块清零
  • 静态内存控制块

    typedef struct
    {
       UINT32           uwBlkSize;          /* 内存块的大小 */
       UINT32           uwBlkNum;           /* 内存块总数 */
       UINT32           uwBlkCnt;           /* 已经分配使用的块数 */
       LOS_MEMBOX_NODE  stFreeList;         /* 内存控制块的空闲链表,指针指向下一个可用的内存块 */
    } LOS_MEMBOX_INFO;
    
  • 静态内存初始化函数LOS_MemboxInit()

    UINT32 LOS_MemboxInit(VOID *pBoxMem,		/* 内存池地址,需要用户自定义 */
                          UINT32 uwBoxSize,		/* 内存池大小 */
                          UINT32 uwBlkSize);	/* 内存块大小 */
    

    初始化后内存示意图:

    LiteOS 内存管理_第3张图片

  • 静态内存申请函数LOS_MemboxAlloc()

    VOID *LOS_MemboxAlloc(VOID *pBoxMem)		/* 指向内存池的指针 */
    

    申请内存示意图如下:
    LiteOS 内存管理_第4张图片

  • 静态内存释放函数LOS_ MemboxFree()

    UINT32 LOS_MemboxFree(VOID *pBoxMem,		/* 内存池地址 */
                          VOID *pBox)			/* 需要释放的内存块 */
    

    内存释放示意图:
    LiteOS 内存管理_第5张图片

  • 静态内存内容清除函数LOS_MemboxClr()

    VOID LOS_MemboxClr(VOID *pBoxMem,		/* 内存池地址 */
                       VOID *pBox)			/* 需要释放的内存块 */
    
  • 注意事项

    • 静态内存池区域,可以通过定义全局数组或调用动态内存分配接口方式获取
    • 如果使用动态内存分配方式,在不需要静态内存池时,注意释放该段内存,避免内存泄露

8.4.2 动态内存使用

**注意:**使用时需要包含“los_memory.h

  • 应用场景

    • 主要是在用户需要使用大小不等的内存块的场景中使用
    • 当用户需要分配内存时,可以通过操作系统的动态内存申请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用
  • 功能函数

    Huawei LiteOS 系统中的动态内存管理模块为用户提供下面几种功能,具体的API详见接口手册。

    功能分类 接口名 描述
    内存初始化 LOS_MemInit 初始化一块指定的动态内存池,大小为size。
    申请动态内存 LOS_MemAlloc 从指定动态内存池中申请size长度的内存。
    释放动态内存 LOS_MemFree 释放已申请的内存。
    重新申请内存 LOS_MemRealloc 按size大小重新分配内存块,并保留原内存块内容。
    内存对齐分配 LOS_MemAllocAlign 从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存。
    分析内存池状态 LOS_MemStatisticsGet 获取指定内存池的统计信息
    查看内存池中最大可用空闲块 LOS_MemGetMaxFreeBlkSize 获取指定内存池的最大可用空闲块
  • 开发流程

    • 配置:
      • OS_SYS_MEM_ADDR:系统动态内存池起始地址,需要用户指定
      • OS_SYS_MEM_SIZE:系统动态内存池大小,以 byte 为单位,需要用户正确计算
      • LOSCFG_MEMORY_BESTFIT:置为 YES,选中内存管理算法中的 BESTFIT 算法
      • LOSCFG_KERNEL_MEM_SLAB:置为 YES,打开内存管理中的SLAB机制
      • SLAB_MEM_COUNT:该配置位于内核中,一般不需要改动,表示 SLAB CLASS 的数量,目前内核初始化为 4
      • SLAB_MEM_ALLOCATOR_SIZE:该配置位于内核中,一般不需要改动,表示每个 SLAB CLASS 的最大可分配的块的总空间
      • SLAB_BASIC_NEED_SIZE:该配置位于内核中,一般不需要改动,表示初始化 SLAB 机制时需要的最小的堆空间。如果改动了 SLAB_MEM_COUNTSLAB_MEM_ALLOCATOR_SIZE 的配置,就需要同步改动这个配置
    • 初始化:
      • 调用 LOS_MemInit 函数初始化用户指定的动态内存池,若用户使能了 SLAB 机制并且内存池中的可分配内存大于 SLAB 需要的最小内存,则会进一步初始化 SLAB CLASS
    • 申请任意大小的动态内存:
      • 调用 LOS_MemAlloc 函数从指定的内存池中申请指定大小的内存块,申请时内存管理先向 SLAB CLASS 申请,申请失败后继续向堆内存空间申请,最后将申请结果返回给用户。在向堆内存空间申请时,会存在内存块的切分
    • 释放动态内存:
      • 调用 LOS_MemFree 函数向指定的动态内存池释放指定的内存块,释放时会先判断该内存块是否属于 SLAB CLASS,若属于,则将该内存块还回 SLAB CLASS。否则,向堆内存空间释放内存块。在向堆内存空间释放时,会存在内存块的合并
  • 动态内存初始化函数LOS_MemInit()

    UINT32 LOS_MemInit(VOID *pPool,				/* 内存开始地址 */
                       UINT32 uwSize)			/* 内存块大小 */
    

LiteOS 内存管理_第6张图片

  • 动态内存申请函数LOS_MemAlloc()

    VOID *LOS_MemAlloc (VOID *pPool,		/* 内存池地址 */
                        UINT32 uwSize)		/* 分配的内存大小 */
    

    申请内存的时候,系统是从内存信息管理节点的 pstTail 指向的内存块开始遍历整个内存块链表查找合适的内存块,如果某个空闲内存块的大小能容得下用户要申请的内存,则将这块内存取出用户需要内存空间大小的部分返回给用户,剩下的内存块组成一个新的空闲块,插入到空闲内存块链表中

    内存分配完成示意图: LiteOS 内存管理_第7张图片

  • 动态内存释放函数LOS_MemFree()

    UINT32 LOS_MemFree (VOID *pPool,			/* 内存池地址 */
    					VOID *pMem)				/* 需要释放的内存地址 */
    

LiteOS 内存管理_第8张图片

  • 注意事项
    • 由于系统中动态内存管理需要消耗管理控制块结构的内存,故实际用户可使用空间总量小于在配置文件 los_config.h 中配置项 OS_SYS_MEM_SIZE 的大小
    • 系统中地址对齐申请内存分配LOS_MemAllocAlign 可能会消耗部分对齐导致的空间,故存在一些内存碎片,当系统释放该对齐时,同时回收由于对齐导致的内存碎片
    • 系统中重新分配内存LOS_MemRealloc 函数如果分配成功,系统会自己判断是否需要释放原来申请的空间,返回重新分配的空间。用户不需要手动释放原来的空间
    • 系统中多次调用LOS_MemFree 时,第一次会返回成功,但对同一块内存进行多次重复释放会导致非法指针操作,导致结果不可预知

你可能感兴趣的:(LiteOS,内存管理)