FreeRTOS内存管理

1 概念

特性 动态方式 静态方式
内存来源 FreeRTOS内存堆 用户预先定义
内存释放 自动回收 不能自动回收
灵活性
使用复杂度 简单 复杂
内存碎片 可能产生 不会产生
实时性 不确定 确定

为何不使用标准C库的malloc/free

复制代码
1.代码空间占用大
    C库的malloc/free实现复杂,代码量大
    嵌入式系统通常资源有限
2.缺乏线程安全性
    标准C库不是为RTOS设计
    没有考虑多任务并发访问
3.实时性差
    执行时间不确定
    不适合实时系统要求的时间确定性
4.内存碎片问题
    长期运行会导致内存碎片
    可能导致内存分配失败

2 内存管理算法

2.1 heap_1

复制代码
只能分配,不能释放
适用于系统启动后只分配不释放的场景
无内存碎片问题
实现简单,时间确定

2.2 heap_2

复制代码
支持分配和释放
使用最佳匹配算法
不能合并相邻空闲块 → 内存碎片
适用于分配固定大小内存块的场景

2.3 heap_3

复制代码
简单封装了标准C库的malloc/free
添加了线程安全保护(挂起调度器)
保留了标准库的所有缺点
仅用于兼容性考虑

2.4 heap_4

复制代码
支持分配和释放
首次适应算法 + 空闲块合并
减少内存碎片
适用于重复创建/删除任务的场景

2.5 heap_5

复制代码
支持分配和释放
可管理多个不连续的内存区域
具有heap_4的所有优点
适用于复杂内存布局的系统
算法 适用场景 优缺点
heap_1 简单应用,任务/资源从不删除 简单快速,但不能释放
heap_2 重复创建删除,但分配块大小相同 可释放,但会产生碎片
heap_3 已有标准库代码需要移植 兼容性好,但性能差
heap_4 通用场景,分配块大小不同 碎片少,最常用
heap_5 内存非连续分布的复杂系统 最灵活,可管理多个内存区

3 相关函数

3.1 pvPortMalloc()

复制代码
void *pvPortMalloc(size_t xWantedSize);
功能描述:从FreeRTOS管理的堆中分配指定大小的内存块。
参数说明:xWantedSize:请求分配的内存大小,以字节为单位
返回值:
    成功:返回指向分配内存的指针
    失败:返回NULL(当可用内存不足时)
注意事项:
1. 对齐考虑
    FreeRTOS会根据架构自动进行内存对齐
    通常为8字节或16字节对齐(取决于portBYTE_ALIGNMENT配置)
2. 实际分配大小
    分配的实际内存可能大于请求的大小,因为:
        内存对齐要求
        需要额外的管理信息
3. 线程安全
    函数内部会自动挂起调度器,保证多任务安全访问

3.2 vPortFree()

复制代码
void vPortFree(void *pv);
功能描述:释放之前通过pvPortMalloc()分配的内存。
参数说明:pv:指向要释放内存的指针
注意事项:
    1. 只能释放由pvPortMalloc分配的内存
    2. 释放后指针应设为NULL,避免野指针
    3. heap_1算法不支持此函数
    4. 释放NULL指针是安全的(不会有任何操作)

3.3 xPortGetFreeHeapSize()

复制代码
size_t xPortGetFreeHeapSize(void);
功能描述:获取当前堆中可用的空闲内存大小(字节数)。
返回值:当前空闲内存大小(以字节为单位)
使用场景:
    1. 内存使用监控
    2. 调试内存泄漏
    3. 动态调整系统行为

4 示例实验

复制代码
/**
 * FreeRTOS演示函数 - 系统入口函数
 * 创建启动任务并启动FreeRTOS调度器
 */
void freertos_demo(void)
{    
    /* 创建启动任务(start_task) */
    xTaskCreate(
        (TaskFunction_t) start_task,              /* 任务函数指针 */
        (char *) "start_task",                    /* 任务名称字符串 */
        (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,  /* 任务栈大小 */
        (void *) NULL,                            /* 任务参数(无) */
        (UBaseType_t) START_TASK_PRIO,            /* 任务优先级 */
        (TaskHandle_t *) &start_task_handler      /* 任务句柄指针 */
    );
    
    /* 启动FreeRTOS调度器,系统开始任务调度 */
    vTaskStartScheduler();
}

/**
 * 启动任务函数
 * 功能:创建其他应用任务,然后自我删除
 * @param pvParameters: 任务参数(未使用)
 */
void start_task(void * pvParameters)
{
    /* 进入临界区 - 禁止任务切换和中断响应 */
    taskENTER_CRITICAL();
    
    /* 创建任务1(task1) */
    xTaskCreate(
        (TaskFunction_t) task1,                   /* 任务函数指针 */
        (char *) "task1",                         /* 任务名称字符串 */
        (configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE, /* 任务栈大小 */
        (void *) NULL,                            /* 任务参数(无) */
        (UBaseType_t) TASK1_PRIO,                 /* 任务优先级 */
        (TaskHandle_t *) &task1_handler           /* 任务句柄指针 */
    );
    
    /* 删除当前启动任务自身(参数NULL表示删除自己) */
    vTaskDelete(NULL);
    
    /* 退出临界区 - 恢复任务切换和中断响应 */
    taskEXIT_CRITICAL();
}

/**
 * 任务1函数 - 内存管理演示任务
 * 功能:通过按键控制内存申请/释放,并定期显示剩余内存
 * @param pvParameters: 任务参数(未使用)
 */
void task1(void * pvParameters)
{
    uint8_t key = 0, t = 0;     /* key: 按键值, t: 定时计数器 */
    uint8_t *buf = NULL;        /* 动态内存缓冲区指针 */
    
    while(1)  /* 任务主循环 */
    {
        /* 扫描按键(模式0) */
        key = key_scan(0);
        
        /* KEY0按下:申请30字节内存 */
        if(key == KEY0_PRES)
        {
            /* 调用FreeRTOS内存分配函数 */
            buf = pvPortMalloc(30);
            
            /* 检查分配是否成功 */
            if(buf != NULL)
            {
                printf("申请内存成功!\r\n");
            }
            else
            {
                printf("申请内存失败!\r\n");
            }
        }
        /* KEY1按下:释放已申请的内存 */
        else if(key == KEY1_PRES)
        {
            if(buf != NULL)
            {
                /* 调用FreeRTOS内存释放函数 */
                vPortFree(buf);
                buf = NULL;        /* 释放后指针置空*/
                printf("释放内存成功!\r\n");
            }
        }
        
        /* 定时器:每(51*500ms≈25.5秒)显示一次剩余内存 */
        if(t++ > 50)
        {
            t = 0;
            /* 获取并显示当前空闲堆大小 */
            printf("剩余的内存大小为:%d\r\n", xPortGetFreeHeapSize());
        }
        
        /* 任务延时500ms,让出CPU控制权 */
        vTaskDelay(500);
    }
}

**实现功能:**系统采用两级任务架构:首先创建一个高优先级的启动任务,该任务在临界区保护下创建实际的应用任务(任务1),然后自我删除以确保系统简洁。任务1作为核心应用,实现了交互式内存管理功能------通过外部按键(KEY0和KEY1)可以动态申请和释放30字节的内存块,并每25.5秒自动显示当前系统的剩余内存大小。

相关推荐
Hello_Embed2 小时前
libmodbus 移植 STM32(基础篇)
笔记·stm32·单片机·学习·modbus
想放学的刺客6 小时前
单片机嵌入式试题(第27期)设计可移植、可配置的外设驱动框架的关键要点
c语言·stm32·单片机·嵌入式硬件·物联网
天昊吖6 小时前
stc8H启用DMA发送后 卡住【踩坑日志】
单片机
李永奉7 小时前
杰理芯片SDK开发-ENC双麦降噪配置/调试教程
人工智能·单片机·嵌入式硬件·物联网·语音识别
BackCatK Chen7 小时前
第 1 篇:软件视角扫盲|TMC2240 软件核心特性 + 学习路径(附工具清单)
c语言·stm32·单片机·学习·电机驱动·保姆级教程·tmc2240
兆龙电子单片机设计7 小时前
【STM32项目开源】STM32单片机多功能电子秤
stm32·单片机·开源·毕业设计·智能家居
切糕师学AI7 小时前
ARM 架构中的复位(Reset)与复位流程
arm开发·单片机·嵌入式·复位
llilian_167 小时前
信号发生器 多通道多功能脉冲信号发生器应用解决方案 多功能脉冲发生器
功能测试·单片机·嵌入式硬件·测试工具
yuanmenghao8 小时前
Classic AUTOSAR深入浅出系列 - 【第十六篇】MCAL:为什么 MCU 换了,上层几乎不用动
单片机·嵌入式硬件·autosar