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秒自动显示当前系统的剩余内存大小。