内存管理介绍
内存管理主要工作是动态的划分并管理用户分配好的内存区间。当程序某一部分需要使用内存,可以通过操作系统的内存申请函数索取指定大小内存块,一旦使用完毕,通过内存释放函数归还所占用内存,使之可以重复使用。在系统运行过程中,内存管理模块通过对内存的申请/释放操作管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优。
内存基本概念
内存块 slice
用户申请到的一片连续内存空间,是内存管理的最小单元。
算法 arithmetic
内存管理的一种策略,如FSC。
内存算法
UniProton 内存管理支持在编译期通过 defconfig 选择底层分配算法。所有算法对外提供统一的标准接口,上层(PRT_MemAlloc/PRT_MemFree/PRT_MemAllocAlign,以及内核各模块直接调用的 OsMemAlloc/OsMemAllocAlign)无需感知具体算法,由公共分派层根据宏控选用。
算法选择与公共分派
- defconfig 选择:
CONFIG_OS_MEM_ARITH_TLSF=y选用 TLSF;CONFIG_OS_MEM_ARITH_BESTFIT_LITTLE=y选用 bestfit_little;CONFIG_OS_MEM_ARITH_BESTFIT=y选用 bestfit;缺省(不设置)用 FSC。 - slab 扩展:
CONFIG_OS_MEM_SLAB_EXTENTION=y在 bestfit_little 和 bestfit 模式下生效,作为小块分配 cache 挂在对应 heap 算法前面,不是独立的 UniProton 内存算法。 - slab 自动扩展:
CONFIG_OS_MEM_SLAB_AUTO_EXPANSION_MODE=y依赖CONFIG_OS_MEM_SLAB_EXTENTION=y,允许 slab class 在初始 bucket 用完后继续从 backing heap 申请 bucket。 - 公共初始化
OsMemInit(addr, size)(src/mem/prt_mem.c):按宏分派到对应算法自己的初始化实现。- FSC:
OsFscMemInit - TLSF:
OsTlsfMemInit - bestfit_little:
OsBestfitLittleMemInit - bestfit:
OsBestfitMemInit
- FSC:
- 各算法的 init 负责初始化内存池,并填充
g_memArithAPI分发表(alloc/allocAlign/free三个函数指针)。 prt_mem.c中的PRT_MemAlloc/PRT_MemFree/PRT_MemAllocAlign经g_memArithAPI表分派到当前算法。- 各板级
OsMemConfigReg只调用公共OsMemInit,不直接指定算法;切换算法仅需改 defconfig,无需改板级代码。
OsMemConfigReg ──> OsMemInit ──┬─ OS_MEM_ARITH_TLSF: OsTlsfMemInit ──> 填 g_memArithAPI
├─ OS_MEM_ARITH_BESTFIT_LITTLE: OsBestfitLittleMemInit ──> 填 g_memArithAPI
├─ OS_MEM_ARITH_BESTFIT: OsBestfitMemInit ──> 填 g_memArithAPI
└─ 否则: OsFscMemInit ──> 填 g_memArithAPI
PRT_MemAlloc/Free/AllocAlign ──> g_memArithAPI.{alloc,free,allocAlign}
内核模块 ──> OsMemAlloc/OsMemAllocAlign(算法无关共享符号,编译期选定算法实现)
公共接口契约
所有算法都必须遵守 UniProton 内存管理公共契约:
- 初始化入口固定为
OsMemInit(addr, size),由公共分派层根据OS_MEM_ARITH_*宏调用具体算法初始化函数。 - 对外接口固定为
PRT_MemAlloc/PRT_MemFree/PRT_MemAllocAlign,内部通过g_memArithAPI分发表调用当前算法。 - 内核内部共享符号固定为
OsMemAlloc/OsMemAllocAlign,由当前编译进来的算法提供实现,避免上层模块感知算法差异。 size == 0返回NULL;分配失败返回NULL;非法释放返回错误码。- 对齐分配必须返回满足
boundary的地址,且释放路径能还原真实节点头。 - 统计信息统一维护到
g_memUsage/g_memPeakUsage/g_memTotalSize/g_memStartAddr,供 memInfo 和测试使用。 - 算法私有入口使用
PRT_前缀;设计文档可以说明来源于 LiteOS,但 UniProton 侧不继续对外暴露LOS_Mem*接口名。
FSC 算法(缺省)
- FSC(Fixed-Size Chunk,定长块):UniProton 缺省内存算法,按空闲块大小建立分级链表,用位图快速定位可用链表。
- 源码:
src/mem/fsc/prt_fscmem.c,实现OsFscMemInit/OsFscMemFree及共享的OsMemAlloc/OsMemAllocAlign。
原理与数据结构
- 每个内存块使用
TagFscMemCtrl描述大小、前一块大小、前后链表指针等元数据。 - 空闲块按大小映射到
g_fscMemNodeList[OS_FSC_MEM_LAST_IDX]。OS_FSC_MEM_SZ2IDX(size)根据最高有效位计算 size class,OS_FSC_MEM_IDX2BIT(idx)映射到g_fscMemBitMap。 g_fscMemBitMap记录哪些 size class 非空,分配时先按申请大小得到目标 class,再通过位图寻找不小于目标大小的空闲链表。- 空闲块插入/删除由
OsFscMemInsert/OsFscMemDelete完成,链表头和位图同步更新。
分配流程
OsMemAlloc对申请大小做最小块和地址对齐处理。- 根据对齐后的大小计算目标 class,结合
g_fscMemBitMap找到可容纳的空闲块。 - 若空闲块大于申请大小且剩余空间足够容纳一个最小块,则调用
OsFscMemSplit分裂。 - 标记节点为 used,返回节点头之后的用户地址。
释放流程
OsFscMemFree根据用户地址还原TagFscMemCtrl节点头。- 校验 used magic、地址范围和块头一致性,非法释放直接返回错误。
- 前后相邻块为空闲时合并,降低外部碎片。
- 将合并后的空闲块重新插入对应 size class 链表,并更新位图。
特点
- 位图 + 分级链表带来较低查找成本,适合默认小型内核场景。
- 节点尾部带
OS_FSC_MEM_TAIL_MAGIC溢出检测,这是 FSC 专有机制;TLSF、bestfit_little、bestfit 不伪造该 magic。 - size class 粒度来自块大小最高有效位,查找快,但相比 best-fit 类算法更容易产生一定内部碎片。
TLSF 算法
- TLSF(Two-Level Segregated Fit,两级分隔适配):通用动态内存算法,通过一级粗粒度桶和二级细粒度桶管理空闲块,目标是在可控碎片下提供有界查找时间。
- 源码目录
src/mem/tlsf/:
| 文件 | 说明 |
|---|---|
prt_tlsf_core.c / prt_tlsf_core.h |
TLSF 算法核心,提供 OsTlsfInit/OsTlsfAlloc/OsTlsfAllocAlign/OsTlsfFree/OsTlsfRealloc/OsTlsfInfoGet 等 |
prt_tlsf_list.h |
算法自用的双向链表(OsTlsfList*/OS_TLSF_DL_LIST*) |
prt_tlsf_compat.h |
类型/打印/hook/任务等适配层,直接对接 UniProton 原生 PRT_ 接口(如 PRT_HwiLock/PRT_Printf/PRT_TaskSelf) |
prt_tlsf_config.h |
算法配置宏(TLSF_CFG_*),裁剪 leakcheck/integrity/task-mem/lms/lmk 等可选项 |
prt_tlsfmem.c |
胶水层:OsTlsfMemInit(注册到 g_memArithAPI)、OsMemAlloc/OsMemAllocAlign/OsTlsfMemFree(包装算法接口 + 维护统计量) |
- 与 FSC 行为对齐:
size==0返回 NULL;对齐boundary小于指针宽度时钳到sizeof(VOID*);维护g_memUsage/g_memPeakUsage/g_memTotalSize/g_memStartAddr统计量供 memInfo 使用。 - 暂不支持 expand(
OS_MEM_EXPAND_ENABLE=0),即不动态扩展内存池。
原理与数据结构
- 内存池头为
OsMemPoolHead,内部包含OsMemPoolInfo、空闲链表数组freeList[OS_MEM_FREE_LIST_COUNT]、一级/二级 bitmap 等信息。 - 每个物理连续节点使用
OsMemNodeHead描述大小、used/aligned/last 等 flag,以及前驱节点指针。 - 空闲节点扩展为
OsMemFreeNodeHead,在节点头后附加空闲链表的prev/next。 - 小块直接映射到 small bucket;大块先按最高有效位得到 first level,再按
OS_MEM_SLI划分 second level,最终映射到一个空闲链表下标。
分配流程
OsTlsfAlloc加锁并检查 pool、size 合法性。OsMemFreeNodeGet根据申请大小映射 FL/SL 下标,先查当前桶,当前桶为空时通过 bitmap 找后续非空桶。- 找到空闲块后从空闲链表删除;若剩余空间大于最小空闲节点,则
OsMemSplitNode分裂出新空闲节点并重新入链。 - 设置 used flag、任务 ID 和水位统计,返回用户地址。
释放流程
OsTlsfFree根据用户地址还原节点头;对齐分配通过 gap size 记录还原真实节点。- 校验地址范围、节点 used flag 和完整性。
- 清除 used flag,并分别尝试和前后相邻空闲节点合并。
- 合并后的节点通过
OsMemFreeNodeAdd插回对应 FL/SL 空闲链表。
特点与约束
- 查找依赖位图和两级桶,复杂度不随空闲块数量线性增长,适合需要稳定分配时延的场景。
- 比 FSC 元数据更复杂,但对不同尺寸申请的碎片控制更细。
- UniProton 当前适配关闭了 expand、LMS、LMK、leakcheck、异常 dump 等依赖外部子系统的可选功能;水位、多池、任务 ID 统计等自包含能力可保留。
bestfit_little 算法与 slab 扩展
- bestfit_little:从 LiteOS bestfit_little 移植到 UniProton 的轻量 best-fit heap 算法,文件按
prt_*命名;算法主体保持原有分配、释放、分裂、合并流程,UniProton 只增加 compat 头、胶水层和必要的接口命名适配。 - 源码目录
src/mem/bestfit_little/:
| 文件 | 说明 |
|---|---|
prt_bestfit_little_memory.c / prt_bestfit_little_heap.c / prt_bestfit_little_memory_internal.h |
bestfit_little 算法主体;原 LiteOS LOS_Mem* 算法入口在 UniProton 内改为 PRT_BestfitLittleMem* |
compat/prt_bestfit_little_*.h |
算法依赖适配到 UniProton:类型、打印、锁、任务统计、配置宏等 |
prt_bestfit_littlemem.c |
胶水层:初始化当前堆、填充 g_memArithAPI、维护 UniProton 统计量、提供 malloc_usable_size |
../slab/prt_slab.c / ../slab/prt_slabmem.c |
共享 slab 小块 cache 实现,通过 adapter 绑定 bestfit_little |
原理与数据结构
- heap 管理结构为
LosHeapManager,记录堆内首尾节点。 - 每个堆节点为
LosHeapNode,保存节点大小、used 状态、前驱/后继关系等元数据。 - 分配策略是 best-fit:遍历可用节点,选择能满足申请且剩余空间最小的空闲块,尽量减少一次分配留下的大碎片。
分配流程
PRT_BestfitLittleMemAlloc先做参数检查和加锁。- 开启 slab 时先尝试
OsSlabMemAlloc,小块命中则直接返回。 - slab 未命中或未开启时进入
OsHeapAlloc,按 best-fit 规则选择最小可用空闲节点。 - 如果选中的节点剩余空间足够容纳新节点,则分裂为 used 节点和剩余 free 节点。
- 设置节点 used 状态和统计信息后返回用户地址。
释放流程
PRT_BestfitLittleMemFree先尝试OsSlabMemFree,命中 slab 则完成释放。- heap 释放通过
((struct LosHeapNode *)ptr) - 1从用户地址还原节点头。 - 校验节点范围、状态和前后关系,非法释放返回错误。
- 将当前节点标记为空闲,并与相邻空闲节点合并。
- slab 架构位置:slab 不是替代 heap 的完整动态内存算法。开启后,bestfit_little 分配入口先尝试
OsSlabMemAlloc分配小块;命不中再走 bestfit_little 的OsHeapAlloc。释放时先尝试OsSlabMemFree,失败再回落OsHeapFree。 - 接口边界:UniProton 对外只暴露标准
PRT_Mem*接口和内核内部OsMem*分发表接口;bestfit_little 算法私有入口使用PRT_BestfitLittleMemInit/PRT_BestfitLittleMemAlloc/PRT_BestfitLittleMemFree等PRT_前缀,不继续暴露 LiteOS 的LOS_Mem*接口名。 - 符号边界:slab 内部使用
OsMemAlloc/OsMemFree作为 backing heap hook;UniProton 已有同名公共接口,因此 compat 层把 slab 内部 hook 重命名为OsPrtHeapAllocForSlab/OsPrtHeapFreeForSlab,避免改变 UniProton 公共架构。 - 64 位适配:bestfit_little 的
OsHeapFree通过((struct LosHeapNode *)ptr) - 1还原节点头。64 位下需要LOSCFG_MEM_TASK_STAT字段使data[0]偏移与sizeof(struct LosHeapNode)保持一致;compat 层开启该字段并通过 UniProton 任务接口提供任务 ID。
bestfit 算法
- bestfit:从 LiteOS bestfit 移植到 UniProton 的完整 bestfit 分配算法,作为独立算法分支由
CONFIG_OS_MEM_ARITH_BESTFIT选择。移植时保持算法主体的分配、释放、分裂、合并和完整性检查流程,UniProton 侧只做命名、依赖适配和公共分派接入。 - 源码目录
src/mem/bestfit/:
| 文件 | 说明 |
|---|---|
prt_bestfit_memory.c / prt_bestfit_memory_internal.h |
bestfit 算法主体;算法私有入口在 UniProton 内统一为 PRT_BestfitMem* |
prt_bestfit_multipledlinkhead.c |
多级空闲链表头选择与初始化 |
compat/prt_bestfit_*.h |
类型、链表、打印、锁、任务、LMS stub、配置宏等 UniProton 适配层 |
prt_bestfitmem.c |
胶水层:初始化当前堆、填充 g_memArithAPI、维护 UniProton 统计量、提供 malloc_usable_size |
../slab/prt_slab.c / ../slab/prt_slabmem.c |
共享 slab 小块 cache 实现,通过 adapter 绑定 bestfit |
原理与数据结构
- 内存池头为
PrtBestfitMemPoolInfo,包含 pool 基本信息、统计信息以及 slab 控制头(开启 slab 时使用)。 - 堆节点为
PrtBestfitMemDynNode,包含selfNode和backupNode两份控制信息;控制节点PrtBestfitMemCtlNode记录 size/flag、前驱节点、空闲链表节点、任务 ID、模块 ID 等。 - 空闲块不是单链遍历,而是挂在多级双向链表头上;
PrtBestfitMultipleDlinkHead和prt_bestfit_multipledlinkhead.c负责按大小选择链表头。 - used/free 状态、对齐 gap、magic、checksum、任务信息都保存在节点控制区中,用于释放、完整性检查和统计。
分配流程
PRT_BestfitMemAlloc检查参数并加锁。- 开启 slab 时先进入
OsSlabMemAlloc,小块命中则跳过 heap。 - heap 分配根据申请大小选择合适的多级空闲链表,查找最匹配的可用节点。
- 选中节点后从空闲链表摘除;剩余空间足够时分裂,并把剩余节点重新插入对应链表。
- 设置 used flag、magic/checksum、任务 ID 和统计信息,返回用户地址。
释放流程
PRT_BestfitMemFree先检查 slab,slab 命中则直接释放。- heap 释放根据用户地址和 gap 信息还原
PrtBestfitMemDynNode。 - 校验节点 magic、checksum、used 状态和地址范围。
- 标记为空闲,和前后相邻空闲节点合并,然后插回多级空闲链表。
- 接口边界:对外仍只暴露标准
PRT_Mem*接口和内核内部共享OsMem*分发表接口;bestfit 算法私有入口使用PRT_BestfitMemInit/PRT_BestfitMemAlloc/PRT_BestfitMemAllocAlign/PRT_BestfitMemFree/PRT_BestfitMemRealloc等PRT_前缀。 - OS 依赖适配:
PrtBestfitCurrTaskGet通过PRT_TaskSelf和PRT_TaskGetInfo获取当前任务 TCB,不再使用空实现;锁、打印、panic、链表和 memcheck 常量由 bestfit 私有 compat 层提供。 - slab 关系:bestfit 与 bestfit_little 一样支持
CONFIG_OS_MEM_SLAB_EXTENTION。slab 仍作为 heap 扩展挂在 bestfit 分配入口前面:小块先尝试OsSlabMemAlloc,命不中再走 bestfit heap;释放时先尝试OsSlabMemFree,失败再回落 bestfit heap free。 - 自动扩展模式:bestfit slab 同样支持
CONFIG_OS_MEM_SLAB_AUTO_EXPANSION_MODE。开启后 slab bucket 从 bestfit heap 申请和释放,算法公共分派与 bestfit heap 主流程不变。 - 64 位适配:slab 私有块头在 UniProton 适配中补齐到 8 字节,保证 AArch64 下
PRT_MemAlloc返回的小块地址满足指针宽度对齐要求;算法的分配/释放/位图管理流程保持不变。
slab 小块扩展
slab 是 bestfit_little 和 bestfit 的小块 cache 扩展能力,不作为独立 OS_MEM_ARITH_* 算法。它的目标是把高频小尺寸申请从通用 heap 路径前置出来,减少小块反复分裂/合并带来的碎片和时延。
原理与数据结构
- slab class 数量为
SLAB_MEM_COUNT=4,步长为SLAB_MEM_CALSS_STEP_SIZE=0x10,默认覆盖 16、32、64、128 字节小块。 OsSlabBlockNode位于用户地址前,用于记录 slab magic、块大小和 record id;释放和malloc_usable_size依赖该头部识别 slab 块。OsSlabAllocator管理同一 item size 的连续 item 区域,AtomicBitset记录 item 是否已使用。OsSlabMem表示一个 slab class,记录块大小、块数量、使用计数。自动扩展模式下还包含OsSlabMemAllocatorbucket 链。- slab 实现只有一份,位于
src/mem/slab/。prt_slab_adapter.h根据OS_MEM_ARITH_BESTFIT_LITTLE或OS_MEM_ARITH_BESTFIT选择对应 compat 头,把控制头类型、状态类型、返回码、自动扩展宏和 backing heap alloc/free hook 适配到当前 heap。 - bestfit_little 和 bestfit 各有自己的 compat 头,分别把控制头嵌入对应 heap pool,不改变 UniProton 公共内存架构。
分配流程
- backing heap 算法入口收到申请后,若
size <= SLAB_MEM_MAX_SIZE,先进入OsSlabMemAlloc。 - 按申请大小选择最小可容纳 class。
- 在 class 对应 allocator 的 bitset 中查找 clear bit 并置位。
OsSlabBlockHeadFill写入 magic 和实际 class 大小,返回 block header 之后的用户地址。- slab class 已满时,固定模式返回
NULL并回落 heap;自动扩展模式尝试申请新 bucket。
释放与检查流程
- 释放时先通过
OS_SLAB_BLOCK_HEAD_GET(ptr)取 slab header。 - 若 magic 不匹配,说明该指针不是 slab 分配,返回失败并交给 backing heap 释放。
- magic 匹配后,根据块大小找到 class,并通过
OsSlabAllocatorFree清 bit。 OsSlabMemCheck用相同逻辑判断指针是否属于 slab,并返回 class 块大小供malloc_usable_size使用。
自动扩展模式
- 宏控:
CONFIG_OS_MEM_SLAB_AUTO_EXPANSION_MODE=y,依赖CONFIG_OS_MEM_SLAB_EXTENTION=y。 - bestfit_little 适配为
LOSCFG_KERNEL_MEM_SLAB_AUTO_EXPANSION_MODE;bestfit 适配为PRT_BESTFIT_CFG_KERNEL_MEM_SLAB_AUTO_EXPANSION_MODE。 - 未开启时,每个 class 只有固定 allocator,用尽后小块申请回落 backing heap。
- 开启后,每个 class 维护 bucket 链;当前 bucket 满时从 backing heap 申请新的
OsSlabMemAllocator和对应OsSlabAllocator。 - 释放后若某个扩展 bucket 已空,且 class 的 bucket 数量大于默认 bucket 数,释放该 bucket,避免 slab 只增不减。
TLSF 配置宏
TLSF 算法的可选项由 prt_tlsf_config.h 的 TLSF_CFG_* 宏控制(prt_tlsf_core.c 中以 #if 守护)。适配时已为所有宏设置安全默认值。下面按「能否开启」分类说明。
当前无法开启(依赖的子系统未适配到 UniProton)
开启下列任一宏会引入当前不存在的依赖,必须保持关闭:
-
OS_MEM_EXPAND_ENABLE(在prt_tlsf_core.c中硬编码为 0)- 功能:内存池耗尽时动态向系统申请新页扩展池容量。
- 缺失依赖:页分配器
OsTlsfPhysPagesAlloc、PAGE_SHIFT、OsTryShrinkMemory。
-
TLSF_CFG_KERNEL_LMS(默认不定义,#ifdef判断)- 功能:LMS(Lite Memory Sanitizer)内存越界 / use-after-free 检测。
- 缺失依赖:LMS 模块。
-
TLSF_CFG_KERNEL_LMK(默认 0)- 功能:LMK,内存耗尽时按策略 kill 任务以回收内存。
- 缺失依赖:
OsTlsfLmkTasksKill。
-
TLSF_CFG_MEM_LEAKCHECK(默认 0)- 功能:内存泄漏检查,为每个分配节点记录调用栈(返回地址)。
- 缺失依赖:回溯接口
OsBackTraceHookCall;配套宏TLSF_CFG_MEM_RECORD_LR_CNT/TLSF_CFG_MEM_OMIT_LR_CNT同样不可开。
-
TLSF_CFG_PLATFORM_EXC(默认 0)- 功能:与异常子系统联动,异常时 dump 内存池信息。
- 缺失依赖:异常 dump 适配模块。
当前可以开启(自包含,无外部依赖)
-
TLSF_CFG_MEM_WATERLINE(默认 1)- 功能:记录内存使用水位(峰值),供统计查询。
-
TLSF_CFG_MEM_MUL_POOL(默认 1)- 功能:支持创建 / 管理多个独立内存池。
-
TLSF_CFG_MEM_MUL_REGIONS(默认 0)- 功能:把多段不连续物理内存加入同一内存池(
OsTlsfRegionsAdd)。
- 功能:把多段不连续物理内存加入同一内存池(
-
TLSF_CFG_TASK_MEM_USED(默认 1)- 功能:在节点头记录申请该内存块的任务 ID。
- 注意:会影响节点头布局;64 位平台必须为 1(否则节点头 padding 会导致
OsTlsfFree取真实指针错位)。
-
TLSF_CFG_MEM_FREE_BY_TASKID(默认 0)- 功能:按任务 ID 释放该任务占用的全部内存。
- 约束:当
TLSF_CFG_TASK_MEM_USED != 1且TLSF_CFG_BASE_CORE_TSK_LIMIT + 1 > 64时编译报错。
-
TLSF_CFG_BASE_MEM_NODE_INTEGRITY_CHECK(默认 0)- 功能:节点完整性检查(魔字 / 链表一致性校验)。
- 注意:可正常编译;完整性失败 panic 路径中的任务信息查询(
OS_TLSF_TCB_FROM_TID)已通过PRT_TaskGetInfo对接 UniProton 任务信息。
-
TLSF_CFG_KERNEL_PRINTF(默认 1)- 功能:TLSF 内部打印开关。
-
TLSF_CFG_BASE_CORE_TSK_LIMIT(默认 31)- 功能:任务数上限,用于 taskID 相关校验。
-
TLSF_CFG_SYS_EXTERNAL_HEAP(默认 1)- 功能:使用外部传入的堆(由 UniProton 的
OS_MEM_FSC_PT_ADDR/SIZE指定),不使用算法自带静态堆数组。
- 功能:使用外部传入的堆(由 UniProton 的
目录结构
src/mem/
prt_mem.c 公共分派层(PRT_Mem* + OsMemInit)
prt_mem_internal.h TagMemFuncLib 分发表定义
include/
prt_mem_external.h 算法无关公共声明(OsMemInit/OsMemAlloc/OsMemAllocAlign)
prt_fscmem_external.h FSC 算法声明
prt_tlsfmem_external.h TLSF 算法声明
prt_bestfit_littlemem_external.h bestfit_little 算法声明
prt_bestfitmem_external.h bestfit 算法声明
fsc/ FSC 算法实现
tlsf/ TLSF 算法实现
bestfit_little/ bestfit_little 算法与 slab 扩展
bestfit/ bestfit 算法实现
slab/ bestfit_little/bestfit 共享 slab 扩展实现与 adapter
新增算法
新增一种内存分配算法(如 bestfit)的步骤:
- 在
src/mem/<algo>/下实现自己的初始化Os<Algo>MemInit与释放Os<Algo>MemFree,以及共享名的OsMemAlloc/OsMemAllocAlign,并在 init 中填充g_memArithAPI分发表。 - 在
src/mem/Kconfig增加config OS_MEM_ARITH_<ALGO>。 - 在
src/mem/CMakeLists.txt增加对应编译分支。 - 在
prt_mem.c的OsMemInit中增加#elif defined(OS_MEM_ARITH_<ALGO>)分派分支。 - 板级 defconfig 设置该宏即可切换,无需改动
OsMemConfigReg。
如果新增的是 slab 这类 heap 扩展能力,不应作为 OS_MEM_ARITH_<ALGO> 独立分支接入;应挂在对应 backing heap 算法内部,并用独立扩展宏控制。
开发流程
步骤一:设置内存管理模块配置项
使用UniProton内存管理模块,需要进行配置项的设置,需要配置的项包括缺省分区首地址、分区大小等。
步骤二:使用内存管理模块
当需要使用内存时,需要先创建一个指定内存管理算法的内存分区,通过调用内存申请接口申请合适大小的内存,就可以对申请到的内存进行操作(包括写操作,然后给其他模块传递消息等);如果是动态内存,当内存使用完,需要对这块内存进行释放,防止发生内存泄漏。
测试
测试分类原则
由于各算法仅底层实现不同、对外标准接口一致,因此绝大部分测试用例是算法无关的(common),FSC、TLSF、bestfit_little、bestfit 等已接入算法都应通过;只有极少数用例依赖某算法专有机制,归为该算法独有:
| 分类 | 说明 | 运行条件 |
|---|---|---|
| common | 经标准接口(PRT_MemAlloc/PRT_MemFree/PRT_MemAllocAlign 或 libc malloc)验证分配/释放/对齐/复用等功能,不依赖任何算法私有布局 |
所有已接入算法都跑 |
| FSC 专有 | 依赖 FSC 私有机制(如节点尾部 OS_FSC_MEM_TAIL_MAGIC 溢出检测) |
仅 FSC 跑 |
| TLSF 专有 | 依赖 TLSF 私有机制 | 仅 TLSF 跑 |
测试 app
| app | 源文件 | 覆盖 | 说明 |
|---|---|---|---|
UniPorton_test_posix_malloc_interface |
runMallocTest.c + posix malloc 用例 |
libc malloc/calloc/realloc/memalign/reallocarray/usable_size 一致性 | common,经 libc→PRT_Mem |
UniPorton_test_prt_mem_interface |
runPrtMemTest.c |
PRT_Mem* 直接接口 |
common,直接验证 PRT_MemAlloc/Free/AllocAlign |
runPrtMemTest 当前 16 个 common 用例:
| 用例 | 验证点 |
|---|---|
| prt_mem_001 | 基本分配 + 写入校验 + 释放 |
| prt_mem_002 | size==0 返回 NULL |
| prt_mem_003 | 释放后再次分配(块复用) |
| prt_mem_004 | PRT_MemAllocAlign 对齐分配 + 地址对齐校验 |
| prt_mem_005 | 多块分配/全释放/合并后大块可用 |
| prt_mem_006 | realloc 扩容/缩容 + 内容保持 |
| prt_mem_007 | 超大 size 申请返回 NULL(OOM 边界) |
| prt_mem_008 | 多种对齐值(4/8/16/32/64)逐一校验 |
| prt_mem_009 | PRT_MemFree(NULL) 返回错误码 |
| prt_mem_010 | 交错分配/释放压力,不损坏堆 |
| prt_mem_011 | 重复释放必须返回错误 |
| prt_mem_012 | 非法对齐枚举返回 NULL |
| prt_mem_013 | malloc_usable_size 普通分配结果覆盖申请大小 |
| prt_mem_014 | 对齐分配可被 malloc_usable_size 正确识别 |
| prt_mem_015 | 小块批量分配释放,覆盖 slab/小块碎片路径,并校验返回地址满足指针宽度对齐 |
| prt_mem_016 | 新建任务上下文中分配/释放,验证任务信息适配 |
算法独有用例
malloc_usable_size_1_1(FSC 专有):校验*(U32*)(ptr + usableSize) == OS_FSC_MEM_TAIL_MAGIC,依赖 FSC 的 tail magic 机制。在runMallocTest.h中仅 FSC 模式登记该用例,TLSF/bestfit_little/bestfit 模式下排除(这些算法无 FSC tail magic 机制,为保证算法原有行为不伪造 magic)。
验证结果(sd3403,armv8)
多种算法均上板验证,结果如下:
| 算法(defconfig) | prt_mem(common 16) | malloc(一致性) |
|---|---|---|
bestfit_little + slab(CONFIG_OS_MEM_ARITH_BESTFIT_LITTLE=y + CONFIG_OS_MEM_SLAB_EXTENTION=y) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
bestfit_little + slab auto expansion(再启用 CONFIG_OS_MEM_SLAB_AUTO_EXPANSION_MODE=y) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
bestfit + slab(CONFIG_OS_MEM_ARITH_BESTFIT=y + CONFIG_OS_MEM_SLAB_EXTENTION=y) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
bestfit + slab auto expansion(再启用 CONFIG_OS_MEM_SLAB_AUTO_EXPANSION_MODE=y) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
bestfit(CONFIG_OS_MEM_ARITH_BESTFIT=y,不启用 slab) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
TLSF(CONFIG_OS_MEM_ARITH_TLSF=y) |
16/16 通过 | 10/10 通过(FSC 专有 usable_size 用例排除) |
| FSC(缺省) | 10/10 通过 | 11/11 通过(含 usable_size) |
结论:common 用例在已接入算法分支均通过;FSC 专有的 malloc_usable_size_1_1 仅在 FSC 模式运行并通过;OsMemInit 公共分派的 FSC/TLSF/bestfit_little/bestfit 分支均已覆盖构建或上板验证。
上板运行(sd3403)
构建(容器内):
cd demos/sd3403/build
sh build_app.sh UniPorton_test_prt_mem_interface
sh build_app.sh UniPorton_test_posix_malloc_interface
切换算法:修改 build/uniproton_config/config_armv8_sd3403/defconfig 中 CONFIG_OS_MEM_ARITH_TLSF 后重新构建。部署/运行/采集串口日志/清理的生命周期管理按 sd3403 deploy 流程(SCP 传 ELF → mica 配置 CPU3 → 触发 → 采集 → mica stop/rm)。