系列文章
FreeRTOS实时操作系统(二)任务创建与任务删除(HAL库)
FreeRTOS实时操作系统(五)进入临界区、任务调度器挂起与恢复
FreeRTOS实时操作系统(七)时间片调度及RTOS的滴答定时器
FreeRTOS实时操作系统(八)任务状态查询及时间统计函数
FreeRTOS实时操作系统(十五)Tickless低功耗模式
内存管理
动态创建:自动地从 FreeRTOS 管理的内存堆中申请创建对象所需的内存,并且在对象删除后,
可将这块内存释放回FreeRTOS管理的内存堆
静态创建:需用户提供各种内存空间,并且使用静态方式占用的内存空间一般固定下来了,即使任务、队列等被删除后,这些被占用的内存空间一般没有其他用途
前面使用的一些创建队列、信号量等,采用的都是动态创建的方式,比起静态的方式,更加简便、灵活。
标准C库也提供了函数malloc()和free()实现动态申请和释放内存,但是没有线程安全的相关机制,内存碎片化等缺点
FreeRTOS提供了五种内存管理算法:(介绍heap_4)
heap_2 内存管理算法使用最适应算法,找出最小的,满足条件的内存碎片,但不能合并空闲内存块
heap_4使用了首次适应算法(第一个满足条件的内存块),也支持内存的申请与释放,并且能够将空闲且相邻的内存进行合并,从而减少内存碎片的现象。
heap_5 内存管理算法是在 heap_4 内存管理算法的基础上实现的,但是 heap_5 内存管理算法在 heap_4 内存管理算法的基础上实现了管理多个非连续内存区域的能力,heap_5 内存管理算法默认并没有定义内存堆 ,需要用户手动指定内存区域的信息,对其进行初始化。
API函数
函数 | 描述 |
---|---|
void * pvPortMalloc( size_t xWantedSize ); | 申请内存 |
void vPortFree( void * pv ); | 释放内存 |
size_t xPortGetFreeHeapSize( void ); | 获取当前空闲内存的大小 |
-
void * pvPortMalloc( size_t xWantedSize );
xWantedSize:申请的内存大小,以字节为单位;
返回值:返回一个指针 ,指向已分配大小的内存。如果申请内存失败,则返回 NULL。 -
void vPortFree( void * pv );
*pv:指针指向一个要释放内存的内存块; -
size_t xPortGetFreeHeapSize( void );
返回值:返回当前剩余的空闲内存大小
实验测试
void task2( void * pvParameters )
{
uint8_t t = 0;
uint8_t * buf = NULL;
while(1)
{
buf = pvPortMalloc(30); /* 申请内存 */
if(buf != NULL)
{
printf("申请内存成功!\r\n");
}
else
printf("申请内存失败\r\n");
if(buf != NULL)
{
vPortFree(buf); /* 释放内存 */
printf("释放内存!!\r\n");
}
if(t > 50)
{
t = 0;
printf("剩余的空闲内存大小为:%d\r\n",xPortGetFreeHeapSize());
}
t++;
vTaskDelay(20);
}
}
测试成功,这里的buf存放的是申请后的地址,所以如果申请一次内存后,多次释放的时候就会出现问题。
这里申请的是30个字节的大小,但是在打印的时候,申请释放前后的总的占用大小差值并不是30,因为还有一些句柄啥的会创建
堆栈溢出
堆栈溢出检查会增加上下文切换的开销,因此建议只在开发或测试阶段使用此检查。
在 RTOS 内核溢出检查开始之前,某些处理器可能会因堆栈损坏而发生故障或异常
方法一:
在 RTOS 内核使任务退出运行状态后,堆栈可能达到其最大(最深)值, 因为此时的堆栈会包含任务上下文。 此时,RTOS 内核可以检查处理器堆栈指针是否仍在有效堆栈空间内。 如果堆栈指针包含超出有效堆栈范围的值, 则调用堆栈溢出钩子函数。
此方法很快,但不能保证可以捕获所有堆栈溢出。 将 configCHECK_FOR_STACK_OVERFLOW 设置为 1 即可使用此方法。
方法二:
任务首次创建时,其堆栈会填充一个已知值。 任务退出运行状态时,RTOS 内核 可以检查有效堆栈范围内的最后 16 个字节,以确保这些已知值未被任务或中断活动 覆盖。 如果这 16 个字节中的任何一个不再为初始值,则调用堆栈溢出钩子函数。
这种方法比方法 1 效率低,但仍然相当快。 它很可能会捕获堆栈溢出, 但仍无法保证能够捕获所有溢出。
将 configCHECK_FOR_STACK_OVERFLOW 设置为 2 即可使用此方法。
方法三:
将 configCHECK_FOR_STACK_OVERFLOW 设置为 3 即可使用此方法。
此方法仅适用于选定的端口。将启用 ISR 堆栈检查。检测到 ISR 堆栈溢出时, 将触发断言。请注意,在这种情况下不会调用堆栈溢出钩子函数, 因为它只针对任务堆栈,而不是 ISR 堆栈。
如果 configCHECK_FOR_STACK_OVERFLOW 未设置为 0 ,则应用程序必须提供堆栈溢出钩子函数。 该钩子函数必须 命名为 vApplicationStackOverflowHook(),并具有以下原型:
void vApplicationStackOverflowHook( TaskHandle_t xTask,
char *pcTaskName );
xTask 和 pcTaskName 参数分别将违规任务的句柄和名称传递给该钩子函数。 但请注意,根据溢出的严重程度,这些参数本身可能会损坏,在这种情况下, 可直接检查 pxCurrentTCB 变量。