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

目录

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 / 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>/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_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);
INT8U    OSMemPut    (OS_MEM *pmem, void *pblk);
INT8U    OSMemQuery  (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 错误体系,增加了调试统计:

void  OSMemCreate (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);
void  OSMemPut    (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。

配置片段:

#define RT_USING_HEAP
#define RT_USING_MEMHEAP
#define RT_USING_MEMPOOL
#define RT_USING_MEMTRACE
#define RT_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_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 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()(可建多个内存池) 可选 bestfitbestfit_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_cachemempool kzalloc/dma_alloc_coherent MMU 全虚拟内存 /proc/slabinfo、kmemleak
FreeRTOS pvPortMalloc/vPortFree heap_4:first-fit+合并 heap_5 HeapRegion_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 memheap rt_memheap_init rt_mp_create/alloc/free(可阻塞) rt_malloc_align Smart:MMU 用户态 memtrace、FinSH free/list_mem
Zephyr k_malloc/k_freek_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 mallinfoCONFIG_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_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/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. 选型建议与最佳实践

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

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐