常见嵌入式操作系统中的内存管理机制
常见嵌入式操作系统中的内存管理机制
目录
- 1. 引言:嵌入式内存管理的特殊性
- 2. 内存管理机制全景
- 2.1 静态内存分配
- 2.2 动态内存堆(变长分配)
- 2.3 固定块内存池(定长分配)
- 2.4 Slab 与对象缓存
- 2.5 虚拟内存与内存保护
- 2.6 跨切关注点
- 3. 各操作系统详解
- 3.1 Linux
- 3.2 FreeRTOS
- 3.3 μC/OS-II
- 3.4 μC/OS-III
- 3.5 RT-Thread
- 3.6 Zephyr
- 3.7 ThreadX(Eclipse ThreadX)
- 3.8 NuttX(Apache)
- 3.9 LiteOS(Huawei LiteOS)
- 3.10 AliOS Things(Rhino 内核)
- 4. 跨 OS 接口对比表
- 5. 硬件支撑:MMU、MPU 与 PMP
- 6. 选型建议与最佳实践
- 7. 总结
1. 引言:嵌入式内存管理的特殊性
内存管理是操作系统最基础的服务之一,但嵌入式场景下的诉求与通用计算有本质差异:
- 资源极其受限:MCU 的 RAM 常以 KB 计,堆管理结构本身的开销(块头、空闲链表指针、对齐填充)都不能忽视。一个 8 字节的块头管理 16 字节的申请,元数据开销就占了 33%。
- 实时确定性优先:通用 malloc 的耗时随堆状态波动(最坏情况不可界),这对硬实时任务是不可接受的。因此嵌入式领域大量使用O(1) 时间复杂度的分配算法(固定块池、TLSF、分级空闲链表)。
- 长期无人值守运行:设备可能连续运行数年,缓慢的堆碎片化累积足以让"明明有空闲总量却申请不到大块"成为现实故障,泄漏检测与碎片控制是可靠性设计的一部分。
- 内存形态多样:同一颗芯片上往往同时存在内部 SRAM、CCM/TCM、外部 SDRAM、PSRAM 等多块地址不连续的 RAM,需要"多堆拼接"机制统一管理。
- 保护能力分层:无 MMU 的 MCU 靠 MPU(ARM)或 PMP(RISC-V)做粗粒度区域保护;带 MMU 的处理器(Cortex-A、部分 RISC-V)则可运行完整的虚拟内存模型。
因此,"嵌入式内存管理"不是单一技术,而是一组按场景组合使用的机制集合。
2. 内存管理机制全景
如上图所示,常见机制可归为四大类,外加一组贯穿始终的跨切关注点。下面逐一讲解。
2.1 静态内存分配
思想:所有内存需求在编译期确定,链接器把对象固定在.data/.bss段中,运行期不存在"分配/释放"动作。
- 编译期全局/静态数组:
static uint8_t buf[4096];是最原始也最可靠的形式。 - OS 对象静态创建:RTOS 把任务控制块(TCB)、栈、队列缓冲区等改由用户在编译期提供。例如 FreeRTOS 的
configSUPPORT_STATIC_ALLOCATION、Zephyr 的K_THREAD_DEFINE、μC/OS 全系列天然就是静态对象模型。 - 链接脚本划定区域:通过 linker script 把特定数组放到特定 RAM(如 DMA 专用 SRAM、掉电保持区)。
优点:零运行时开销、零碎片、零失败路径,启动即确定内存上限,便于安全认证。
缺点:灵活性差,必须按峰值需求预留,内存利用率低。
实践经验:高可靠产品(汽车电子、医疗设备)常要求"初始化完成后禁止一切动态分配",本质上就是运行期全静态化。
2.2 动态内存堆(变长分配)
堆(heap)管理一块连续(或拼接后逻辑连续)的 RAM,支持任意尺寸的申请与释放,核心矛盾是速度、碎片、开销三者的权衡。
2.2.1 空闲链表 + First-Fit / Best-Fit
最经典的实现:空闲块串成链表,每块头部记录大小与状态。
- First-Fit:从头扫描,取第一个足够大的块,速度快但易在低地址端堆积小碎片。
- Best-Fit:取最接近请求尺寸的块,碎片略少但扫描更慢、且容易产生难以利用的"碎屑"。
- 释放时合并(Coalescing):通过块头/块尾信息找到物理相邻块,若空闲则合并,缓解外部碎片。
FreeRTOS 的 heap_4、NuttX 的 mm、μC/OS 之外的大多数 RTOS 默认堆都是这一族的变体。
2.2.2 TLSF 与分级空闲链表(Segregated Fit)
TLSF(Two-Level Segregated Fit)用两级位图索引一系列按尺寸分级的空闲链表:第一级按 2 的幂分段,第二级在每段内线性细分。查找"最小可用块"只需两次位运算找最高位(fls/ffs),分配与释放都是严格 O(1),且碎片率有理论界。RT-Thread 的 lwp 用户态堆、Zephyr 的 sys_heap、多数现代 RTOS 的可选分配器都采用此类思想。AliOS Things 的 Rhino 内核 mm 采用的分级空闲链表 + binmap 位图加速,亦是同一设计哲学。
2.2.3 伙伴系统(Buddy)
把内存按 2 的幂尺寸管理,申请时逐级分裂、释放时与"伙伴"块合并,合并/分裂都是 O(log n) 且有很强的大块保持能力,适合页粒度管理(Linux 物理页分配器的核心),缺点是对任意字节请求的内部碎片大(申请 100B 要给 128B)。
2.2.4 多堆 / 多区域扩展
MCU/SoC 上多块不连续 RAM(内部 SRAM + CCM + 外部 SDRAM)需要拼接成一个逻辑堆:登记一张{起始地址, 大小}区域表,分配器跨表项维护统一空闲结构。
对应实现:FreeRTOS heap_5 的HeapRegion_t、RT-Thread 的 memheap、NuttX 的CONFIG_MM_REGIONS、Zephyr 允许多个k_heap实例并存。
2.3 固定块内存池(定长分配)
把一块内存预先切成 N 个等长块,空闲块用侵入式链表串起来:申请 = 摘链表头,释放 = 插回链表头,严格 O(1)、无外部碎片、无合并逻辑,是硬实时系统的主力军。
代价是内部碎片:申请 20B 也要占用一个 128B 的块。工程上的解法是多规格池——同时建 32B/64B/128B/256B 几档池,按尺寸路由。μC/OS 的OS_MEM、ThreadX 的 block pool、RT-Thread 的 mempool、Zephyr 的k_mem_slab、LiteOS 的 membox 都是该机制。
2.4 Slab 与对象缓存
Slab 是固定块池的"内核对象版":为每类高频内核对象(inode、task_struct)建专用缓存,块内预初始化对象结构,分配/回收免去重复构造。Linux 的 slab/slub/slob、RT-Thread 的 slab 堆算法、Zephyr 的 mem slab 都属于此族。其价值在于:
- 复用对象构造结果,降低初始化成本;
- 同类对象集中存放,Cache 亲和性更好;
- 便于按对象类型统计用量与追踪泄漏。
2.5 虚拟内存与内存保护
- MMU + 分页:虚拟地址经页表翻译到物理页帧,附带页级权限(R/W/X)与缺页异常,支撑进程隔离、按需调页、共享内存、swap。是 Linux、NuttX(部分平台)、Zephyr(x86_64/ARM64)、ThreadX Modules 等"带进程"系统的底座。
- MPU(ARM Cortex-M/R)/ PMP(RISC-V):不做地址翻译,只设置若干(通常 8~16 个)区域的基址/大小/权限,越界访问触发 MemManage/Fault 异常。用于任务栈保护、外设隔离、内核/用户态访问限制,典型如 FreeRTOS-MPU、Zephyr userspace、ThreadX 的 MPU 支持。
2.6 跨切关注点
无论选用哪类机制,以下问题都要单独设计:
- 碎片控制:外部碎片靠合并/伙伴/TLSF 缓解,内部碎片靠多规格池缓解;长期运行系统应在设计期评估碎片模型,必要时干脆禁用变长堆。
- 实时确定性:硬实时路径上只用 O(1) 分配器(固定块池/TLSF);变长 first-fit 堆的耗时不可界,只应用于初始化阶段。
- 对齐:DMA 缓冲常要求 4/8/32 字节甚至 Cache 行(32/64B)对齐,多数 OS 提供
*_alloc_align类接口;带 D-Cache 的平台还要考虑一致性维护(clean/invalidate)。 - 统计与诊断:用量峰值(high-water mark)、当前/累计分配计数、泄漏检测(记录申请点调用栈/行号)、堆完整性校验(金丝雀值/块头魔数)。
- 多核竞争:SMP 下堆是全局共享资源,分配器内部用自旋锁或关中断保护;高并发场景倾向 per-CPU 缓存(Linux slub 的 per-cpu partial 思路)。
3. 各操作系统详解
3.1 Linux
Linux 拥有本文中最完整的内存管理体系,分内核态与用户态两层。
3.1.1 内核态内存管理
| 层次 | 机制 | 核心接口 | 说明 |
|---|---|---|---|
| 物理页分配 | 伙伴系统(Buddy) | alloc_pages()、__get_free_pages()、free_pages() | 按 order(2ⁿ 页)分配物理连续页;页框由struct page描述,按 zone(DMA/NORMAL/HIGHMEM)划分 |
| 小对象分配 | SLAB / SLUB / SLOB | kmalloc()/kfree()(通用缓存)、kmem_cache_create()/kmem_cache_alloc()(专用缓存) | 现代内核默认 SLUB;SLOB 面向极小内存设备,6.8 起已被移除 |
| 大块/非连续 | vmalloc 区 | vmalloc()/vfree() | 虚拟地址连续、物理页可离散,适合大缓冲;不可用于需要物理连续的 DMA |
| 连续物理内存 | CMA(Contiguous Memory Allocator) | dma_alloc_coherent()、设备树reserved-memory | 启动时预留,按需迁移/压实出物理连续大块,服务于摄像头、GPU 等 |
| 预分配池 | mempool | mempool_create()/mempool_alloc() | 为关键路径(块设备 IO)预存对象,保证内存耗尽时仍可推进 |
| 页回收 | LRU + kswapd + 直接回收 | — | 内存不足时回收 page cache/匿名页(换出);OOM killer 兜底 |
| 内存压缩 | zswap / zram | — | 嵌入式设备常用 zram 以 CPU 换内存 |
3.1.2 用户态内存管理
- 进程地址空间:每个进程独立的页表 + VMA(
vm_area_struct)红黑树管理代码段、堆、mmap 区、栈。 - 系统调用:
brk/sbrk(堆顶移动)、mmap/munmap(文件/匿名映射)、mprotect(改权限)、madvise(使用模式提示)。 - C 库分配器:glibc ptmalloc(多 arena 缓解多线程竞争);嵌入式常用 musl 的 malloc-ng,或可替换为 jemalloc/tcmalloc。
- 诊断工具:
/proc/<pid>/maps、smem、valgrind/ASan(调试期)、kmemleak(内核泄漏扫描)、/proc/buddyinfo、/proc/slabinfo。
3.1.3 嵌入式裁剪关注点
嵌入式 Linux 的典型动作:用 SLUB 替代 SLAB;开启 CMA 为多媒体外设预留连续内存;用 zram 在有限 RAM 上换取可用内存;通过vm.min_free_kbytes、/proc/sys/vm/overcommit_*调整水位与超售策略;无 MMU 的处理器(如 Cortex-M 上的 uClinux 传统路线)则退化为无虚拟内存的平坦模型,现代实践多改用带 MMU 的 Cortex-A 平台。
3.2 FreeRTOS
FreeRTOS 内核本身不规定分配算法,而是提供5 个可替换的 heap 实现(heap_1.c~heap_5.c),由用户链接时选一个,统一暴露pvPortMalloc()/vPortFree()。内核对象(TCB、队列、信号量)的内存既可来自该堆(configSUPPORT_DYNAMIC_ALLOCATION),也可完全由用户静态提供(configSUPPORT_STATIC_ALLOCATION)。
| 实现 | 算法 | 特点 | 适用 |
|---|---|---|---|
| heap_1 | 只分配,不释放 | 最简单、确定性好,一次性静态切分 | 对象创建后永不删除的系统 |
| heap_2 | 可释放,Best-Fit,不合并相邻空闲块 | 会产生碎片,已被官方标记为遗留(保留仅为兼容) | 不推荐新项目使用 |
| heap_3 | 包装 C 库malloc/free,加临界区保护 | 行为依赖 libc,线程安全由 FreeRTOS 保证 | 快速原型 |
| heap_4 | First-Fit + 相邻空闲块合并 | 碎片可控、常用默认;分配耗时不可严格界定 | 大多数应用 |
| heap_5 | heap_4 算法 +多区域(vPortDefineHeapRegions()登记HeapRegion_t数组) | 可拼接内部 SRAM/CCM/外部 RAM | 多 RAM 芯片 |
关键配置与接口:
configTOTAL_HEAP_SIZE/* 堆总大小(heap_1/2/4 用 ucHeap 数组) */configSUPPORT_STATIC_ALLOCATION/* 静态对象:xTaskCreateStatic() 等 */configSUPPORT_DYNAMIC_ALLOCATION/* 动态对象 */pvPortMalloc(size);vPortFree(ptr);xPortGetFreeHeapSize();/* 当前空闲 */xPortGetMinimumEverFreeHeapSize();/* 历史最低水位(评估堆是否过大) */malloc_failed_hook/vApplicationMallocFailedHook()/* 分配失败钩子 */另有FreeRTOS-MPU变体:利用 Cortex-M MPU 把任务分为特权/非特权级,限制任务可访问的内存区域,栈溢出可触发硬件异常。生态上 Amazon FreeRTOS 之后,该项目由 AWS 支持演进(2024 年起 FreeRTOS 内核仓库迁移至独立社区治理)。
3.3 μC/OS-II
μC/OS-II不提供变长堆,内存管理的官方答案是内存分区(Memory Partition)——正是 §2.3 的固定块池:
OS_MEM*OSMemCreate(void*addr,INT32U nblks,INT32U blksize,INT8U*err);void*OSMemGet(OS_MEM*pmem,INT8U*err);INT8UOSMemPut(OS_MEM*pmem,void*pblk);INT8UOSMemQuery(OS_MEM*pmem,OS_MEM_DATA*pdata);实现要点:
- 用户划出一块连续 RAM,告诉内核块数与块长;内核用侵入式单链表管理空闲块,
OSMemGet/OSMemPut即摘/插链表头,O(1) 且可在中断中使用(OSMemGet失败不阻塞,返回错误码)。 OS_MEM_DATA可查询空闲块数/已用块数,用于运行时监控。- 变长需求只能借道 C 库
malloc(不推荐在实时路径使用)或自建"多档分区"(如 32/64/128B 三个 partition 按尺寸路由)。
设计哲学非常鲜明:宁可让用户管理多档固定池,也不给不可确定耗时的变长堆。这与其安全关键市场(医疗、航空认证版本)定位一致。
3.4 μC/OS-III
μC/OS-III 继承 II 代的内存分区机制,接口更名并统一到OS_ERR错误体系,增加了调试统计:
voidOSMemCreate(OS_MEM*p_mem,CPU_CHAR*p_name,void*p_addr,OS_MEM_QTY n_blks,OS_MEM_SIZE blk_size,OS_ERR*p_err);void*OSMemGet(OS_MEM*p_mem,OS_ERR*p_err);voidOSMemPut(OS_MEM*p_mem,void*p_blk,OS_ERR*p_err);- 每个分区是全局
OSMemQty管理的OS_MEM对象,带名字便于调试器(μC/Probe)展示。 - 支持任务内嵌统计:配合
OSStatTaskCPUUsage等,可做系统级资源画像。 - 与 II 代相同:内核对象数量由编译期/初始化期确定(
OSCfg_...Max),整体仍是"静态对象 + 固定池"模型,无内置变长堆。 - 开启
OS_CFG_DBG_EN后可用OSMemDbgTbl遍历全部分区,便于泄漏排查。
3.5 RT-Thread
RT-Thread 提供三层内存设施,灵活度在 RTOS 中最高:
| 设施 | 开关 | 核心接口 | 机制 |
|---|---|---|---|
| 动态堆 heap | RT_USING_HEAP | rt_malloc()、rt_free()、rt_realloc()、rt_calloc()、rt_malloc_align()/rt_free_align() | 默认 small memory 算法(小内存优化);RT_USING_SLAB换 slab 算法(大 RAM 更高效) |
| 多堆拼接 memheap | RT_USING_MEMHEAP | rt_memheap_init()、rt_memheap_alloc()、rt_memheap_free() | 把多块不连续 RAM 挂成一个逻辑堆 |
| 内存池 mempool | RT_USING_MEMPOOL | rt_mp_create()、rt_mp_alloc()、rt_mp_free() | 固定块池,支持申请时阻塞等待(挂起队列) |
实现要点:
- small memory:双向链表组织空闲块,块头带魔数便于完整性检查,释放时前后向合并;针对小 RAM 优化元数据开销。
- slab 模式:借鉴 Solaris slab,zone 内分级 chunk 管理,适合 RAM 较大的场景(Smart 系列)。
- memheap:各区域先独立成堆,
rt_memheap_init加入全局堆集合,分配时优先匹配,适合片内 SRAM + 外部 SDRAM 组合。 - 诊断:
RT_USING_MEMTRACE记录每次分配的调用点;list_mem/free(FinSH 命令)查看用量;RT_MEM_STATS输出统计。 - RT-Thread Smart:带 MMU 的用户态版本,用户进程通过 lwp 获得类 Linux 的虚拟地址空间,用户堆基于 TLSF。
配置片段:
#defineRT_USING_HEAP#defineRT_USING_MEMHEAP#defineRT_USING_MEMPOOL#defineRT_USING_MEMTRACE#defineRT_USING_MEMHEAP_AS_HEAP/* 多堆统一作为系统堆 */3.6 Zephyr
Zephyr 的内存管理设施分系统堆、私有堆、定长分配器、用户态内存域四条线:
| 设施 | 配置 / 类型 | 核心接口 | 说明 |
|---|---|---|---|
| 系统堆 | CONFIG_HEAP_MEM_POOL_SIZE | k_malloc()、k_free()、k_aligned_alloc()、k_calloc() | 全局共享堆,底层为 sys_heap,多线程安全(自旋锁保护) |
| 通用堆 | struct sys_heap | sys_heap_init()、sys_heap_alloc()、sys_heap_free()、sys_heap_aligned_alloc() | 底层分配器:分级空闲链表 + 最近使用缓存,分配接近 O(1);自身不带锁,由上层(k_heap/系统堆)加锁 |
| 私有堆 | struct k_heap | k_heap_init()、k_heap_alloc()、k_heap_free() | 带锁堆对象,可建多个实例各自管理一块 RAM,便于按用途隔离 |
| 定长分配 | K_MEM_SLAB_DEFINE | k_mem_slab_init()、k_mem_slab_alloc()(可带超时的阻塞申请)、k_mem_slab_free() | 固定块池,编译期可静态定义 |
| 多级块池 | sys_mem_blocks | sys_mem_blocks_alloc()等 | 多级位图块分配器,块大小为 2 的幂倍率,介于 slab 与堆之间 |
| 用户态隔离 | CONFIG_USERSPACE | k_mem_domain/k_mem_partition、K_MEM_PARTITION_DEFINE、k_mem_domain_add_thread() | 基于 MPU/MMU 的内存分区:线程只能访问被授权的分区,系统调用跨越特权级 |
| 按需调页 | CONFIG_DEMAND_PAGING | — | 在 x86_64/ARM64 上支持缺页时才回填,配合后备存储实现超分配 |
补充设施:mem_guard/栈金丝雀(CONFIG_STACK_CANARIES)检测栈溢出;k_heap运行统计(sys_heap_runtime_stats_get,需CONFIG_SYS_HEAP_RUNTIME_STATS)给出分配次数、空闲字节、最大块等。Zephyr 还定义了 devicetree 级 SRAM/PSRAM 分区属性,可把特定 buffer 定位到指定 RAM 域。
3.7 ThreadX(Eclipse ThreadX)
ThreadX(微软于 2023 年捐赠给 Eclipse 基金会,现名 Eclipse ThreadX)的内存服务只有两类对象,但打磨得极为工程化:
| 对象 | 创建 / 使用接口 | 机制 |
|---|---|---|
| 字节池 Byte Pool(变长) | tx_byte_pool_create()、tx_byte_allocate()、tx_byte_release()、tx_byte_pool_info_get() | 空闲块有序链表 + First-Fit,释放时与相邻块合并;支持分配挂起(TX_WAIT_FOREVER等待内存可用) |
| 块池 Block Pool(定长) | tx_block_pool_create()、tx_block_allocate()、tx_block_release()、tx_block_pool_info_get() | 固定块池,O(1),同样支持阻塞等待 |
特点:
- 池对象由
TX_BYTE_POOL/TX_BLOCK_POOL控制块描述,可创建任意多个,按用途分池(网络缓冲一个池、协议栈一个池),隔离故障域。 tx_byte_pool_info_get()返回总字节、可用字节、碎片数等,配套 TraceX 可视化内存事件。- 字节池分配是 first-fit,耗时随碎片增长,官方文档明确建议时间关键路径只用块池。
- ThreadX Modules:在带 MMU 的处理器上加载位置无关模块(类似进程/动态库),模块拥有独立内存空间与 MPU 保护。
- ThreadX 家族以安全认证著称(IEC 61508/61508 SIL4、IEC 62304、ISO 26262 ASIL D 等预认证),"静态对象 + 块池"模型天然契合认证要求的可分析性。
3.8 NuttX(Apache)
NuttX 定位为"POSIX 化的小型 OS",内存管理按构建模式分层:
| 构建/设施 | 接口 | 说明 |
|---|---|---|
| Flat build(单地址空间) | malloc()/free()/realloc()/memalign() | 全局一个堆,mm_initialize()初始化 |
| Protected/Kernel build | kmm_malloc()/kmm_free()(内核堆)、umm_malloc()等(用户堆) | 双堆隔离:内核堆与用户堆分开管理,用户态经系统调用陷入 |
| 通用 mm 框架 | mm_initialize()、mm_addregion()、mm_malloc()、mm_free() | 底层分配器:空闲块按地址有序链表,First-Fit + 释放合并;CONFIG_MM_REGIONS支持多区域拼接 |
| 粒状分配器 granule | gran_initialize()、gran_alloc()、gran_free() | 按固定页粒(granule)分配,常用于 DMA 连续缓冲与页大小资源 |
| 内存池 | mempool_init()、mempool_allocate()、mempool_release() | 固定块池,支持扩展与阻塞 |
| 按需调页 | CONFIG_PAGING | 少数平台支持缺页回填 |
调试:CONFIG_MM_BACKTRACE记录分配回溯;mallinfo()/mallinfo_task()按任务统计用量(配合 NuttX 的任务分组记账);CONFIG_MM_SMALL为小块优化块头开销。NuttX 的独特性在于:在保持 RTOS 身段的同时,提供了接近 Linux 的"内核堆/用户堆 + POSIX 接口"体验,移植 Linux 用户态代码的摩擦很小。
3.9 LiteOS(Huawei LiteOS)
Huawei LiteOS(OpenHarmony 轻量内核及 IoT 产品常用)分动态堆与静态池两套:
| 设施 | 核心接口 | 机制 |
|---|---|---|
| 动态堆 | LOS_MemAlloc()、LOS_MemFree()、LOS_MemRealloc()、LOS_MemAllocAlign()、LOS_MemPoolInit()(可建多个内存池) | 可选bestfit与bestfit_little两种算法(LOSCFG_KERNEL_MEM_BESTFIT_LITTLE等配置):bestfit 兼顾碎片与速度;bestfit_little 面向小 RAM,元数据更小 |
| 静态池 membox | LOS_MemboxInit()、LOS_MemboxAlloc()、LOS_MemboxClr()、LOS_MemboxFree() | 固定块池,O(1) |
诊断能力较全:LOS_MemTotalUsedGet()/LOS_MemPoolSizeGet()查用量,LOS_MemIntegrityCheck()做堆完整性校验,水线统计LOS_MemMaxUsedGet(),开启LOSCFG_MEM_LEAKCHECK后记录每次申请的 LR/任务号用于泄漏定位。LiteOS-A(面向带 MMU 的 Cortex-A,OpenHarmony 小型/标准系统内核)另有完整虚拟内存:进程地址空间、按需调页、共享内存、用户/内核双堆;LiteOS-M 则面向无 MMU 微内核场景,与上述动态堆 + membox 模型一致。
3.10 AliOS Things(Rhino 内核)
AliOS Things 的 Rhino 内核内存管理(k_mm)设计取向是低耗时确定性:
- 算法:分级空闲链表(segregated free list)+binmap 位图两级索引(小格线性区 + 2 的幂对数区),查找最小可用块只需位扫描指令,分配/释放接近 O(1)——与 TLSF 同源思想。
- 接口(内核层):
krhino_init_mm_head()初始化内存堆、krhino_add_mm_region()追加不连续区域(多堆)、krhino_mm_alloc()/krhino_mm_free()/krhino_mm_realloc()。 - 接口(系统层):
aos_malloc()、aos_zalloc()、aos_realloc()、aos_free()对上层组件统一封装,屏蔽内核差异。 - 调试:开启 mm debug 后每个块记录申请任务与调用点,支持泄漏扫描与水线统计;
krhino_mm_leak_region_chk类接口做区域巡检。 - 定位:无 MMU 的 IoT 场景为主,配合 uMesh/Linkkit 等组件栈使用;内存池类需求通常直接用 mm 或组合静态数组实现。
4. 跨 OS 接口对比表
| OS | 变长堆接口 | 堆算法 | 多区域堆 | 固定块池 | 对齐分配 | 用户态/虚拟内存 | 统计与诊断 |
|---|---|---|---|---|---|---|---|
| Linux(内核) | kmalloc/vmalloc/alloc_pages | 伙伴 + SLUB | 天然支持(node/zone) | kmem_cache、mempool | kzalloc/dma_alloc_coherent | MMU 全虚拟内存 | /proc/slabinfo、kmemleak |
| FreeRTOS | pvPortMalloc/vPortFree | heap_4:first-fit+合并 | heap_5HeapRegion_t | 无内置(用户自建或 heap_1 静态切分) | 无(自行封装) | FreeRTOS-MPU 区域保护 | xPortGetFreeHeapSize、最低水位、失败钩子 |
| μC/OS-II | 无内置堆 | — | — | OSMemCreate/Get/Put | 无 | 无 | OSMemQuery |
| μC/OS-III | 无内置堆 | — | — | OSMemCreate/Get/Put | 无 | 无 | OSMemDbgTbl、统计任务 |
| RT-Thread | rt_malloc/rt_free/rt_realloc | small memory / slab | memheaprt_memheap_init | rt_mp_create/alloc/free(可阻塞) | rt_malloc_align | Smart:MMU 用户态 | memtrace、FinSHfree/list_mem |
| Zephyr | k_malloc/k_free、k_heap_* | sys_heap:分级空闲链表 | 多k_heap实例 | k_mem_slab_*、sys_mem_blocks | k_aligned_alloc | userspace + 内存域 + 按需调页 | sys_heap_runtime_stats_get、栈金丝雀 |
| ThreadX | tx_byte_allocate(字节池) | first-fit+合并 | 多池实例 | tx_block_allocate(块池) | 创建池时指定对齐缓冲 | Modules(MMU/MPU) | *_pool_info_get、TraceX |
| NuttX | malloc/free(flat)、kmm_/umm_ | 有序链表 first-fit+合并 | CONFIG_MM_REGIONS | granule、mempool | memalign | protected/kernel build 双堆;部分平台 paging | mallinfo、CONFIG_MM_BACKTRACE |
| LiteOS | LOS_MemAlloc/Free/Realloc | bestfit / bestfit_little | LOS_MemPoolInit多池 | LOS_MemboxInit/Alloc/Free | LOS_MemAllocAlign | LiteOS-A:虚拟内存 | 水线、完整性校验、泄漏检测 |
| AliOS Things | aos_malloc/aos_free、krhino_mm_* | 分级空闲链表 + binmap(类 TLSF) | krhino_add_mm_region | 无专用对象(用 mm/静态数组) | 底层支持对齐 | 无 | mm debug 泄漏扫描、水线 |
注:表中"无内置堆"不代表不能用 C 库 malloc,而是实时路径不应依赖它。
5. 硬件支撑:MMU、MPU 与 PMP
内存机制的上限由硬件决定,三大阵营的对比如下:
| 硬件 | 代表架构 | 能力 | OS 侧对应 |
|---|---|---|---|
| 无保护单元 | 低端 Cortex-M0/M3、多数 8/16 位 MCU | 全部代码同一地址空间,野指针直接踩硬件 | 纯静态/池化策略 + 软件断言;μC/OS、裸机式 FreeRTOS |
| MPU(ARMv7-M/ARMv8-M) | Cortex-M3/M4/M7/M33/M55 | 8~16 个内存区域,基址+大小+权限(X 禁止执行、AP 访问级),越界触发 MemManage 异常 | FreeRTOS-MPU、Zephyr userspace、ThreadX 的任务保护 |
| PMP(RISC-V) | 各 RV32/RV64 MCU 核 | 物理内存区域保护,条目数实现相关(常见 16),配合 M/S/U 特权级 | Zephyr/RT-Thread 的 RISC-V 移植、NuttX RV 端口 |
| MMU | Cortex-A 系列、RV64GC、x86 | 页表翻译、页级权限、TLB、ASID,支撑虚拟内存 | Linux、NuttX kernel build、Zephyr(ARM64/x86_64)、ThreadX Modules、RT-Thread Smart |
经验法则:
- Cortex-M 无 MPU 款(M0/M0+,如 PY32 系列):别指望硬件保护,把可靠性押在"静态分配 + 固定池 + 严格 code review"上。
- 带 MPU 的 M3/M4/M7/M33:至少给每个任务配栈保护区(栈底放一个 no-access region),溢出当场抓异常,比事后查死机划算得多。
- Cortex-A / RV64:直接用虚拟内存模型,注意 DMA 缓冲走 CMA/coherent 接口,Cache 一致性由 OS 维护。
- RISC-V PMP:粒度与 MPU 类似,但注意早期 MCU 核 PMP 条目可能只有 4~8 个,规划区域时要精打细算。
6. 选型建议与最佳实践
- 初始化期用堆,运行期用池:上电初始化阶段允许
malloc式变长分配(失败直接不启动,可接受);进入主循环后只从预建的固定块池取内存,故障可预期。 - 按用途分池、按尺寸分档:网络缓冲、协议消息、GUI 对象各建各的池,配合 32/64/128/256B 多档,既隔离故障域又压低内部碎片。
- 硬实时路径只碰 O(1):中断和高优先级控制环里只用固定块池/TLSF 类分配器;first-fit 堆的耗时随碎片化程度漂移。
- 给堆留出诊断接口:无论用哪个 OS,第一时间打开水线统计(FreeRTOS 最低水位、Zephyr runtime stats、LiteOS 水线、RT-Thread memtrace),上线前压测出峰值,堆大小按"峰值 × 1.3"配置。
- 多 RAM 芯片先规划区域再选堆:DMA 缓冲放哪、TCM 放什么热数据、外部 SDRAM 谁用,画在链接脚本里,再用 heap_5/memheap/多 k_heap 拼接,别让分配器"盲选"。
- 带 D-Cache 的平台对齐到 Cache 行:DMA 缓冲用
*_align接口按 32/64B 对齐并独占一行,避免 false sharing 导致的一致性维护误伤相邻数据。 - 安全认证产品向 μC/OS、ThreadX 的模型靠拢:静态对象 + 固定池,运行期无分配失败路径,验证与认证成本最低。
- 长期运行设备定期做堆健康巡检:完整性校验 + 碎片率(最大空闲块/总空闲)监控,超阈值告警,把"运行三年后申请失败"消灭在测试阶段。
7. 总结
嵌入式内存管理的主线可以归纳为一句话:用确定性换灵活性。
- 资源越紧、实时性越硬,越倾向静态分配与固定块池(μC/OS、ThreadX 是极端代表);
- 需要通用性与生态时,引入变长堆,但用 TLSF/分级链表把耗时压到近 O(1)(Zephyr、AliOS Things、RT-Thread);
- 多块不连续 RAM 用多堆拼接统一视图(heap_5、memheap、MM_REGIONS);
- 有 MMU 就进入虚拟内存世界(Linux、NuttX kernel build、RT-Thread Smart),换取进程隔离与内存利用率的全面解放;
- 无 MMU 但带 MPU/PMP 的平台,至少把任务栈保护和特权隔离用起来。
十个系统的设计差异,本质上是它们在"灵活性—确定性—开销"三角形中选的位置不同。理解了这张机制全景图,面对任何一个新 OS 的内存接口,都能迅速定位它属于哪一族、代价是什么、该怎么用。
