常见嵌入式操作系统中的内存管理机制

常见嵌入式操作系统中的内存管理机制

目录

  • 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. 引言:嵌入式内存管理的特殊性

内存管理是操作系统最基础的服务之一,但嵌入式场景下的诉求与通用计算有本质差异:

  1. 资源极其受限:MCU 的 RAM 常以 KB 计,堆管理结构本身的开销(块头、空闲链表指针、对齐填充)都不能忽视。一个 8 字节的块头管理 16 字节的申请,元数据开销就占了 33%。
  2. 实时确定性优先:通用 malloc 的耗时随堆状态波动(最坏情况不可界),这对硬实时任务是不可接受的。因此嵌入式领域大量使用O(1) 时间复杂度的分配算法(固定块池、TLSF、分级空闲链表)。
  3. 长期无人值守运行:设备可能连续运行数年,缓慢的堆碎片化累积足以让"明明有空闲总量却申请不到大块"成为现实故障,泄漏检测与碎片控制是可靠性设计的一部分。
  4. 内存形态多样:同一颗芯片上往往同时存在内部 SRAM、CCM/TCM、外部 SDRAM、PSRAM 等多块地址不连续的 RAM,需要"多堆拼接"机制统一管理。
  5. 保护能力分层:无 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 跨切关注点

无论选用哪类机制,以下问题都要单独设计:

  1. 碎片控制:外部碎片靠合并/伙伴/TLSF 缓解,内部碎片靠多规格池缓解;长期运行系统应在设计期评估碎片模型,必要时干脆禁用变长堆。
  2. 实时确定性:硬实时路径上只用 O(1) 分配器(固定块池/TLSF);变长 first-fit 堆的耗时不可界,只应用于初始化阶段。
  3. 对齐:DMA 缓冲常要求 4/8/32 字节甚至 Cache 行(32/64B)对齐,多数 OS 提供*_alloc_align类接口;带 D-Cache 的平台还要考虑一致性维护(clean/invalidate)。
  4. 统计与诊断:用量峰值(high-water mark)、当前/累计分配计数、泄漏检测(记录申请点调用栈/行号)、堆完整性校验(金丝雀值/块头魔数)。
  5. 多核竞争: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 / SLOBkmalloc()/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 等
预分配池mempoolmempool_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>/mapssmemvalgrind/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_4First-Fit + 相邻空闲块合并碎片可控、常用默认;分配耗时不可严格界定大多数应用
heap_5heap_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 中最高:

设施开关核心接口机制
动态堆 heapRT_USING_HEAPrt_malloc()rt_free()rt_realloc()rt_calloc()rt_malloc_align()/rt_free_align()默认 small memory 算法(小内存优化);RT_USING_SLAB换 slab 算法(大 RAM 更高效)
多堆拼接 memheapRT_USING_MEMHEAPrt_memheap_init()rt_memheap_alloc()rt_memheap_free()把多块不连续 RAM 挂成一个逻辑堆
内存池 mempoolRT_USING_MEMPOOLrt_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_SIZEk_malloc()k_free()k_aligned_alloc()k_calloc()全局共享堆,底层为 sys_heap,多线程安全(自旋锁保护)
通用堆struct sys_heapsys_heap_init()sys_heap_alloc()sys_heap_free()sys_heap_aligned_alloc()底层分配器:分级空闲链表 + 最近使用缓存,分配接近 O(1);自身不带锁,由上层(k_heap/系统堆)加锁
私有堆struct k_heapk_heap_init()k_heap_alloc()k_heap_free()带锁堆对象,可建多个实例各自管理一块 RAM,便于按用途隔离
定长分配K_MEM_SLAB_DEFINEk_mem_slab_init()k_mem_slab_alloc()(可带超时的阻塞申请)、k_mem_slab_free()固定块池,编译期可静态定义
多级块池sys_mem_blockssys_mem_blocks_alloc()多级位图块分配器,块大小为 2 的幂倍率,介于 slab 与堆之间
用户态隔离CONFIG_USERSPACEk_mem_domain/k_mem_partitionK_MEM_PARTITION_DEFINEk_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 buildkmm_malloc()/kmm_free()(内核堆)、umm_malloc()等(用户堆)双堆隔离:内核堆与用户堆分开管理,用户态经系统调用陷入
通用 mm 框架mm_initialize()mm_addregion()mm_malloc()mm_free()底层分配器:空闲块按地址有序链表,First-Fit + 释放合并;CONFIG_MM_REGIONS支持多区域拼接
粒状分配器 granulegran_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()(可建多个内存池)可选bestfitbestfit_little两种算法(LOSCFG_KERNEL_MEM_BESTFIT_LITTLE等配置):bestfit 兼顾碎片与速度;bestfit_little 面向小 RAM,元数据更小
静态池 memboxLOS_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_cachemempoolkzalloc/dma_alloc_coherentMMU 全虚拟内存/proc/slabinfo、kmemleak
FreeRTOSpvPortMalloc/vPortFreeheap_4:first-fit+合并heap_5HeapRegion_t无内置(用户自建或 heap_1 静态切分)无(自行封装)FreeRTOS-MPU 区域保护xPortGetFreeHeapSize、最低水位、失败钩子
μC/OS-II无内置堆OSMemCreate/Get/PutOSMemQuery
μC/OS-III无内置堆OSMemCreate/Get/PutOSMemDbgTbl、统计任务
RT-Threadrt_malloc/rt_free/rt_reallocsmall memory / slabmemheaprt_memheap_initrt_mp_create/alloc/free(可阻塞)rt_malloc_alignSmart:MMU 用户态memtrace、FinSHfree/list_mem
Zephyrk_malloc/k_freek_heap_*sys_heap:分级空闲链表k_heap实例k_mem_slab_*sys_mem_blocksk_aligned_allocuserspace + 内存域 + 按需调页sys_heap_runtime_stats_get、栈金丝雀
ThreadXtx_byte_allocate(字节池)first-fit+合并多池实例tx_block_allocate(块池)创建池时指定对齐缓冲Modules(MMU/MPU)*_pool_info_get、TraceX
NuttXmalloc/free(flat)、kmm_/umm_有序链表 first-fit+合并CONFIG_MM_REGIONSgranule、mempoolmemalignprotected/kernel build 双堆;部分平台 pagingmallinfoCONFIG_MM_BACKTRACE
LiteOSLOS_MemAlloc/Free/Reallocbestfit / bestfit_littleLOS_MemPoolInit多池LOS_MemboxInit/Alloc/FreeLOS_MemAllocAlignLiteOS-A:虚拟内存水线、完整性校验、泄漏检测
AliOS Thingsaos_malloc/aos_freekrhino_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/M558~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 端口
MMUCortex-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. 选型建议与最佳实践

  1. 初始化期用堆,运行期用池:上电初始化阶段允许malloc式变长分配(失败直接不启动,可接受);进入主循环后只从预建的固定块池取内存,故障可预期。
  2. 按用途分池、按尺寸分档:网络缓冲、协议消息、GUI 对象各建各的池,配合 32/64/128/256B 多档,既隔离故障域又压低内部碎片。
  3. 硬实时路径只碰 O(1):中断和高优先级控制环里只用固定块池/TLSF 类分配器;first-fit 堆的耗时随碎片化程度漂移。
  4. 给堆留出诊断接口:无论用哪个 OS,第一时间打开水线统计(FreeRTOS 最低水位、Zephyr runtime stats、LiteOS 水线、RT-Thread memtrace),上线前压测出峰值,堆大小按"峰值 × 1.3"配置。
  5. 多 RAM 芯片先规划区域再选堆:DMA 缓冲放哪、TCM 放什么热数据、外部 SDRAM 谁用,画在链接脚本里,再用 heap_5/memheap/多 k_heap 拼接,别让分配器"盲选"。
  6. 带 D-Cache 的平台对齐到 Cache 行:DMA 缓冲用*_align接口按 32/64B 对齐并独占一行,避免 false sharing 导致的一致性维护误伤相邻数据。
  7. 安全认证产品向 μC/OS、ThreadX 的模型靠拢:静态对象 + 固定池,运行期无分配失败路径,验证与认证成本最低。
  8. 长期运行设备定期做堆健康巡检:完整性校验 + 碎片率(最大空闲块/总空闲)监控,超阈值告警,把"运行三年后申请失败"消灭在测试阶段。

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 的内存接口,都能迅速定位它属于哪一族、代价是什么、该怎么用。