11-内存管理.md 5.1 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第19章 + 配套代码21" author: "尚硅谷研究院" date: 2026-07-12 url: ""

created: 2026-07-12

内存管理

用生活理解:内存管理就像图书馆的座位管理——

  • heap_1:图书馆只进不出,每个人来了就固定坐一个位置,永远不走(最简单)
  • heap_2:可以走了,但走之后留下的空位不合并——时间长了满是小缝隙,大桌子放不下
  • heap_4:有人走了,把旁边空位合并成大空间(推荐!)
  • heap_5:图书馆有多个独立区域,可以跨区域管理座位

为什么 FreeRTOS 不用标准 malloc?

在嵌入式系统中,标准 C 库的 malloc()/free() 有这些问题:

  1. 不一定可用——很多 MCU 开发环境没带完整 C 库
  2. 占用代码空间——printf、malloc 全家桶很大
  3. 非线程安全——两个任务同时 malloc 可能崩溃
  4. 非确定性——malloc 执行时间不确定(RTOS 要求确定!)

五种 heap 算法对比

算法 策略 合并空闲块 允许释放 适用场景 推荐?
heap_1 简单数组分配 不适用 ❌ 不能释放 只创建不删除的项目
heap_2 最佳适应(Best-fit) 任务栈大小固定 ❌ 已过时
heap_3 C 库 malloc/free 由 C 库决定 标准库可用时
heap_4 首次适应(First-fit) 通用场景 ⭐ 推荐
heap_5 跨区域 First-fit ✅(跨区域) 内存地址不连续

heap_1:最简单(不能释放)

"蹲坑不冲水":只申请不释放,就像上厕所不冲水——简单粗暴,但只适用于项目启动时一次性创建所有对象的场景。

初始化: [         整个堆空间          ]
分配后: [ Task1 ][ Task2 ][ Task3 ][ 剩余 ]

一次分配、永不释放。适用于项目启动时创建所有任务/队列,运行中不删除任何东西。

heap_2:最佳适应(过时)

"伤过的心已经变成玻璃碎片":最佳适应算法(Best-fit)从满足要求的块中选择最接近请求大小的。问题是不合并相邻空闲块——时间长满是小碎片,大块分配失败。

释放后: [ Task1 ][ 空闲 ][ Task3 ][ 空闲 ]
         ↑不合并          ↑不合并
        变成小碎片        变成小碎片
分配16字节:选"最适合"的块(不一定是最大的)
问题:碎片越来越多,最终大块分配失败

heap_3:C 库包装

标准 C 库 malloc()/free() 的简单线程安全包装。如果开发环境自带 C 库就可以用,但和裸机 malloc 一样有非确定性问题。

heap_4:第一适应 + 合并(推荐)

首次适应算法(First-fit):按地址顺序找到第一个足够大的块就用,不追求最优匹配(更快)。自动合并相邻空闲块——有人走了,把旁边空位合并成大空间。

释放后: [ Task1 ][ 空闲 ][ Task3 ][ 空闲 ]
         ↓自动合并相邻空闲块
         [ Task1 ][   大空闲空间   ]
分配时:选"第一个足够大的"块

最多碎片控制:相邻空闲块自动合并,减少碎片。

heap_5:跨区域 heap_4

"杀鸡焉用牛刀":能合并不连续的内存区域,但开销大。除非你的内存物理上就不连续(比如外扩 SARM 和片内 SRAM 要统一管理),否则用 heap_4 就够了。

内存布局: [ 区域A 128KB ]    [ 区域B 64KB ]    [ 区域C 32KB ]
           ↑ 不连续但heap_5能统一管理

示例:内存管理(项目21)

核心代码

void task1(void *pvParameters)
{
    uint8_t key = 0;
    void *buff = NULL;

    while (1)
    {
        key = Key_Detect();

        if (key == KEY1_PRESS)
        {
            // 申请20字节内存
            buff = pvPortMalloc(20);
            if (buff != NULL) {
                printf("申请20字节成功!剩余堆=%d\r\n",
                       xPortGetFreeHeapSize());
            }
        }
        else if (key == KEY2_PRESS)
        {
            // 释放内存
            vPortFree(buff);
            printf("释放成功!剩余堆=%d\r\n",
                   xPortGetFreeHeapSize());
        }

        vTaskDelay(500);
    }
}

内存 API

函数 作用 类比
pvPortMalloc(size) 申请 size 字节 跟图书馆说"我要坐这儿"
vPortFree(ptr) 释放之前申请的内存 离开座位
xPortGetFreeHeapSize() 查还剩多少堆空间 看看图书馆还有多少空位

配套代码

  • code/21_内存管理/

相关:[[03-FreeRTOS移植指南]](移植时需选择 heap_4.c)