4.2 内存

    基本概念

    在系统运行过程中,内存管理模块通过对内存的申请/释放操作,来管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。

    Huawei LiteOS的内存管理分为静态内存管理和动态内存管理,提供内存初始化、分配、释放等功能。

    • 动态内存:在动态内存池中分配用户指定大小的内存块。

      • 优点:按需分配。

      • 缺点:内存池中可能出现碎片。

    • 静态内存:在静态内存池中分配用户初始化时预设(固定)大小的内存块。

      • 优点:分配和释放效率高,静态内存池中无碎片。

      • 缺点:只能申请到初始化预设大小的内存块,不能按需申请。

    动态内存运作机制

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

    与静态内存相比,动态内存管理的好处是按需分配,缺点是内存池中容易出现碎片。

    LiteOS动态内存支持DLINK和BEST LITTLE两种标准算法。

    1. DLINK

    DLINK动态内存管理结构如下图所示:

    第一部分: 堆内存(也称内存池)的起始地址及堆区域总大小。

    第二部分: 本身是一个数组,每个元素是一个双向链表,所有free节点的控制头都会被分类挂在这个数组的双向链表中。

    假设内存允许的最小节点为2min字节,则数组的第一个双向链表存储的是所有size为2min\min+1的free节点,第二个双向链表存储的是所有size为2min+1\min+2的free节点,依次类推第n个双向链表存储的是所有size为2min+n-1\min+n的free节点。每次申请内存的时候,会从这个数组检索最合适大小的free节点,进行分配内存。每次释放内存时,会将该片内存作为free节点存储至这个数组,以便下次再利用。

    第三部分: 占用内存池极大部分的空间,是用于存放各节点的实际区域。以下是节点结构体申明以及简单介绍:

    4.2.内存 - 图2

    2.BEST LITTLE

    LiteOS的动态内存分配支持最佳适配算法,即BEST LITTLE,每次分配时选择内存池中最小最适合的内存块进行分配。LiteOS动态内存管理在最佳适配算法的基础上加入了SLAB机制,用于分配固定大小的内存块,进而减小产生内存碎片的可能性。

    LiteOS内存管理中的SLAB机制支持可配置的SLAB CLASS数目及每个CLASS的最大空间,现以SLAB CLASS数目为4,每个CLASS的最大空间为512字节为例说明SLAB机制。在内存池中共有4个SLAB CLASS,每个SLAB CLASS的总共可分配大小为512字节,第一个SLAB CLASS被分为32个16字节的SLAB块,第二个SLAB CLASS被分为16个32字节的SLAB块,第三个SLAB CLASS被分为8个64字节的SLAB块,第四个SLAB CLASS被分为4个128字节的SLAB块。这4个SLAB CLASS是从内存池中按照最佳适配算法分配出来的。

    初始化内存管理时,首先初始化内存池,然后在初始化后的内存池中按照最佳适配算法申请4个SLAB CLASS,再逐个按照SLAB内存管理机制初始化4个SLAB CLASS。

    每次申请内存时,先在满足申请大小的最佳SLAB CLASS中申请,(比如用户申请20字节内存,就在SLAB块大小为32字节的SLAB CLASS中申请),如果申请成功,就将SLAB内存块整块返回给用户,释放时整块回收。如果满足条件的SLAB CLASS中已无可以分配的内存块,则继续向内存池按照最佳适配算法申请。需要注意的是,如果当前的SLAB CLASS中无可用SLAB块了,则直接向内存池申请,而不会继续向有着更大SLAB块空间的SLAB CLASS申请。

    释放内存时,先检查释放的内存块是否属于SLAB CLASS,如果是SLAB CLASS的内存块,则还回对应的SLAB CLASS中,否则还回内存池中。

    静态内存运作机制

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

    静态内存池由一个控制块和若干相同大小的内存块构成。控制块位于内存池头部,用于内存块管理。内存块的申请和释放以块大小为粒度。

    4.2.内存 - 图4
    静态内存示意图

    开发指导

    使用场景

    内存管理的主要工作是动态的划分并管理用户分配好的内存区间。

    当用户需要分配内存时,可以通过操作系统的动态内存申请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。

    功能

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

    1. 配置:

    OS_SYS_MEM_ADDR:系统动态内存池起始地址,一般不需要修改。

    OS_SYS_MEM_SIZE:系统动态内存池大小,以byte为单位,系统默认分配DDR后未使用的空间。

    LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:内存越界检测开关,默认关闭。打开后,每次申请动态内存时执行动态内存块越界检查;每次释放静态内存时执行静态内存块越界检查。

    1. 初始化LOS_MemInit。

    初始一个内存池后如图,生成一个 EndNode,并且剩余的内存全部被标记为FreeNode节点。

    EndNode作为内存池末尾的节点,size为0。
    4.2.内存 - 图6

    1. 申请任意大小的动态内存LOS_MemAlloc。

    判断动态内存池中是否存在申请量大小的空间,若存在,则划出一块内存块,以指针形式返回,若不存在,返回NULL。

    调用三次LOS_MemAlloc函数可以创建三个节点,假设名称分别为UsedA,UsedB,UsedC,大小分别为sizeA,sizeB,sizeC。因为刚初始化内存池的时候只有一个大的FreeNode,所以这些内存块是从这个FreeNode中切割出来的。

    当内存池中存在多个FreeNode的时候进行malloc,将会适配最合适大小的FreeNode用来新建内存块,减少内存碎片。若新建的内存块不等于被使用的FreeNode的大小,则在新建内存块后,多余的内存又会被标记为一个新的FreeNode。

    1. 释放动态内存LOS_MemFree。

    回收内存块,供下一次使用。

    假设调用LOS_MemFree释放内存块UsedB,则会回收内存块UsedB,并且将其标记为FreeNode。
    4.2.内存 - 图8

    BEST LITTLE开发流程
    1. 配置:

    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_COUNT和SLAB_MEM_ALLOCATOR_SIZE的配置,就需要同步改动这个配置。

    1. 初始化:

    调用LOS_MemInit函数初始化用户指定的动态内存池,若用户使能了SLAB机制并且内存池中的可分配内存大于SLAB需要的最小内存,则会进一步初始化SLAB CLASS。

    1. 申请任意大小的动态内存:

    调用LOS_MemAlloc函数从指定的内存池中申请指定大小的内存块,申请时内存管理先向SLAB CLASS申请,申请失败后继续向堆内存空间申请,最后将申请结果返回给用户。在向堆内存空间申请时,会存在内存块的切分。

    1. 释放动态内存:

    调用LOS_MemFree函数向指定的动态内存池释放指定的内存块,释放时会先判断该内存块是否属于SLAB CLASS,若属于,则将该内存块还回SLAB CLASS。否则,向堆内存空间释放内存块。在向堆内存空间释放时,会存在内存块的合并。

    平台差异性

    无。

    注意事项

    • 系统中地址对齐申请内存分配LOS_MemAllocAlign可能会消耗部分对齐导致的空间,故存在一些内存碎片,当系统释放该对齐内存时,同时回收由于对齐导致的内存碎片。

    • 系统中重新分配内存LOS_MemRealloc函数如果分配成功,系统会自己判定是否需要释放原来申请的空间,返回重新分配的空间。用户不需要手动释放原来的空间。

    • 系统中多次调用LOS_MemFree时,第一次会返回成功,但对同一块内存进行多次重复释放会导致非法指针操作,导致结果不可预知。

    编程实例

    实例描述

    Huawei LiteOS运行期间,用户需要频繁的使用内存资源,而内存资源有限,必须确保将有限的内存资源分配给急需的程序,同时释放不用的内存。

    通过Huawei LiteOS内存管理模块可以保证高效、正确的申请、释放内存。

    1. 初始化一个动态内存池。

    2. 在动态内存池中申请一个内存块。

    3. 使用这块内存块存放一个数据。

    4. 打印出存放在内存块中的数据。

    5. 释放掉这块内存。

    编程实例
    结果验证

    结果显示

    开发指导

    使用场景

    当用户需要使用固定长度的内存时,可以使用静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。

    功能

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

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

    本节介绍使用静态内存的典型场景开发流程。

    1. 规划一片内存区域作为静态内存池。

    2. 调用LOS_MemboxInit接口。

      系统内部将会初始化静态内存池。将入参指定的内存区域分割为N块(N值取决于静态内存总大小和块大小),将所有内存块挂到空闲链表,在内存起始处放置控制头。

    3. 调用LOS_MemboxAlloc接口。

      系统内部将会从空闲链表中获取第一个空闲块,并返回该块的用户空间地址。

    4. 调用LOS_MemboxFree接口。

      将该块内存加入空闲块链表。

    5. 调用LOS_MemboxClr接口。

      系统内部清零静态内存块,将入参地址对应的内存块清零。

    平台差异性

    无。

    注意事项

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

    编程实例

    实例描述

    Huawei LiteOS运行期间,用户需要频繁的使用内存资源,而内存资源有限,必须确保将有限的内存资源分配给急需的程序,同时释放不用的内存。

    通过内存管理模块可以保证正确且高效的申请释放内存。

    本实例执行以下步骤:

    1. 初始化一个静态内存池。

    2. 从静态内存池中申请一块静态内存。

    3. 使用这块内存块存放一个数据。

    4. 打印出存放在内存块中的数据。

    5. 清除内存块中的数据。

    6. 释放掉这块内存。

    编程实例
    结果验证