FreeRTOS内存,任务管理

FreeRTOS 内存管理

:heap,就是一块空闲的内存,需要提供管理函数
malloc:从堆里划出一块空间给程序使用
free:用完后,再把它标记为 空闲 的,可以再次使用

:stack,函数调用时局部变量保存在栈中,当前程序的环境也是保存在栈中,可以从堆中分配一块空间用作栈。
在这里插入图片描述

FreeRTOS中内存管理的接口函数为:pvPortMalloc 、vPortFree,对应于C库的malloc、free。 文件在FreeRTOS/Source/portable/MemMang

void *pvPortMalloc( size_t xWantedSize );

xWantedSize: 要分配的内存块的大小(以字节为单位)。
如果分配成功,返回指向分配的内存块的指针。如果分配失败(例如内存不足),返回 NULL

void vPortFree( void *pv )

vPortFree 用于释放之前使用 pvPortMalloc 分配的内存块。它将内存块返回到FreeRTOS堆,以便将来重新使用。
pv: 指向要释放的内存块的指针。这个指针必须是之前通过调用 pvPortMalloc 获得的。传递

在FreeRTOS中,有几种不同的堆管理方案,它们提供了从简单到复杂的内存管理功能。下面详细介绍了各个堆管理方案的特性、优缺点和适用场景:

堆管理方案文件特性优点缺点适用场景
Heap 1heap_1.c- 固定大小内存块。
- 只支持分配,不支持释放。
- 实现简单,代码量小。
- 分配速度快。
- 不支持释放内存,容易造成内存浪费。- 简单应用。
- 确定内存需求的嵌入式系统。
Heap 2heap_2.c- 动态分配和释放。
- 不合并相邻的空闲块。
- 实现简单。
- 适合于需要简单分配和释放的场景。
- 不合并空闲块,可能会导致内存碎片。- 小型应用。
- 碎片问题不严重的场景。
Heap 3heap_3.c- 封装了标准的 mallocfree- 直接使用标准库的内存管理功能。- 依赖于标准库的实现,不一定适合所有嵌入式系统。- 使用标准库的环境。
- 兼容性要求高的系统。
Heap 4heap_4.c- 动态分配和释放。
- 合并相邻的空闲块。
- 减少了内存碎片。
- 适合复杂应用。
- 实现较复杂,代码量大。
- 分配和释放速度较慢。
- 需要频繁分配和释放内存的场景。
Heap 5heap_5.c- 支持多个非连续的内存区域。
- 动态分配和释放。
- 支持更大灵活性。
- 合并相邻的空闲块,减少碎片。
- 实现复杂,适用范围广但可能需要更多的配置。- 大型应用。
- 有多个内存区域的复杂系统。

相关函数介绍

size_t xPortGetFreeHeapSize( void );

用于返回系统当前剩余的可用堆内存的大小(以字节为单位)。可以用来优化内存的使用情况

size_t xPortGetMinimumEverFreeHeapSize( void );

用于返回系统自启动以来,堆内存的最低空闲量(以字节为单位)。只有heap_4、heap_5支持此函数

Malloc 钩子函数 pvPortMalloc函数内部实现

void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
    ......
    #if ( configUSE_MALLOC_FAILED_HOOK == 1 )
        {
            if( pvReturn == NULL )
            {
                extern void vApplicationMallocFailedHook( void );
                vApplicationMallocFailedHook();
            }
        }
    #endif
    
    return pvReturn;        
}

malloc 失败的钩子函数是一种机制,用于处理内存分配失败的情况。当系统中的内存不足以满足 pvPortMalloc(或 malloc)请求的分配时,该钩子函数会被调用。它的主要作用是提供一种自定义的处理方式,以应对内存分配失败的情况。

如何使用钩子函数

在FreeRTOS中,可以通过配置和实现 vApplicationMallocFailedHook 钩子函数来处理内存分配失败的情况。以下是具体的配置和实现步骤:

  1. 配置FreeRTOS: 在FreeRTOS的配置文件 FreeRTOSConfig.h 中,启用 configUSE_MALLOC_FAILED_HOOK 宏:

    #define configUSE_MALLOC_FAILED_HOOK 1
    
  2. 实现钩子函数: 在你的应用程序代码中,实现 vApplicationMallocFailedHook 函数。这个函数没有返回值和参数,自定义的处理代码放在这个函数中。例如:

    void vApplicationMallocFailedHook(void)
    {
        // 记录错误日志
        printf("Memory allocation failed!\n");
    
        // 重启系统或其他恢复操作
        // vTaskDelete(NULL); // 假设删除当前任务以释放资源
        // NVIC_SystemReset(); // 假设重启系统
    }
    

FreeRTOS 任务管理

FreeRTOS中,我们可以创建多个任务(task),任务也可以称为线程(thread)。

任务创建与删除

​ FreeRTOS 中使用xTaskCreate用来创建任务的函数。它允许用户定义任务的行为、名称、栈大小、优先级、任务句柄等。在创建任务时,开发人员需要提供任务的入口函数、任务名称、栈大小、传递给任务的参数、任务优先级和任务句柄。以下是对 xTaskCreate 函数的详细介绍。

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
                        const char * const pcName, // 任务的名字
                        const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
                        void * const pvParameters, // 调用任务函数时传入的参数
                        UBaseType_t uxPriority,    // 优先级
                        TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

TaskFunction_t pxTaskCode:指向任务函数的指针。该任务函数是任务的入口点,它的原型为 void vTaskFunction(void *pvParameters),即必须接受一个 void * 类型的参数,并且没有返回值,永不退出。

const char * const pcName:任务的名字,主要用于调试目的。不同任务名称应该尽量唯一,以方便识别和调试。

const configSTACK_DEPTH_TYPE usStackDepth:栈大小类型,通常为 uint16_t。任务栈的大小,以字(word)为单位。例如,如果传入 100,表示栈的大小为 100 word,即 400 字节。

void * const pvParameters:指向常量的指针,传递给任务函数的参数。在任务函数中可以通过该参数访问任务创建时的上下文信息。

UBaseType_t uxPriority:(优先级类型,通常为 uint32_t)任务的优先级,数值越高优先级越高。任务的优先级范围为 0~(configMAX_PRIORITIES – 1)。优先级决定了任务的调度顺序和响应时间。

  • 类型:UBaseType_t(优先级类型,通常为 uint32_t
  • 描述:任务的优先级,数值越高优先级越高。优先级决定了任务的调度顺序和响应时间。

TaskHandle_t * const pxCreatedTask:保存 xTaskCreate 的输出结果,即任务的句柄(task handle)。任务句柄用于后续操作,如删除任务、挂起任务等。可以传入 NULL,如果不需要以后操作该任务。

使用静态分配内存的函数如下:

TaskHandle_t xTaskCreateStatic ( 
    TaskFunction_t pxTaskCode,   // 函数指针, 任务函数
    const char * const pcName,   // 任务的名字
    const uint32_t ulStackDepth, // 栈大小,单位为word,10表示40字节
    void * const pvParameters,   // 调用任务函数时传入的参数
    UBaseType_t uxPriority,      // 优先级
    StackType_t * const puxStackBuffer, // 静态分配的栈,就是一个buffer
    StaticTask_t * const pxTaskBuffer // 静态分配的任务结构体的指针,用它来操作这个任务
);

相比于使用动态分配内存创建任务的函数,最后2个参数不一样

StackType_t * const puxStackBuffer: 静态分配的栈内存,比如可以传入一个数组, 它的大小是usStackDepth*4。
StaticTask_t * const pxTaskBuffer: 静态分配的StaticTask_t结构体的指针,指向静态分配的任务控制块(TCB)。需要分配并初始化一个 StaticTask_t 结构体,并将该结构体的指针传递给该参数。
返回值 成功:返回任务句柄; 失败:NULL

任务的删除

void vTaskDelete( TaskHandle_t xTaskToDelete );

pvTaskCode:任务句柄,使用xTaskCreate创建任务时可以得到一个句柄。 也可传入NULL,这表示删除自己。

任务暂停状态(Suspended)

FreeRTOS中的任务通过vTaskSuspend函数可以进入暂停状态。

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

xTaskToSuspend表示要暂停的任务,如果为NULL,表示暂停自己。

要退出暂停状态,只能由别的任务来操作:vTaskResume,xTaskResumeFromISR(中断函数调用)

任务就绪状态(Ready)

这个任务随时可以运行:只是有其他高优先级的任务在运行还。这时,它就处于就绪态(Ready)。

状态转换图
在这里插入图片描述

空闲任务及其钩子函数

​ 在 FreeRTOS 中,空闲任务(Idle Task) 是一个特殊的任务,它的优先级最低,负责执行系统中的后台工作。空闲任务永远不会被删除,它会在没有其他任务需要运行时执行。空闲任务通常用于系统的后台清理工作,例如回收被删除任务的资源。为了实现特定功能,可以通过配置空闲任务钩子函数来定制空闲任务的行为。

空闲任务(Idle Task)

功能和特性

  1. 最低优先级:空闲任务的优先级最低,只有当没有其他任务需要运行时,空闲任务才会被调度。

  2. 资源回收:默认情况下,空闲任务会回收被删除任务的栈内存和任务控制块(TCB),从而释放系统资源。

  3. 不可删除:空闲任务是由系统自动创建的任务,用户不能删除它。

  4. 后台工作:用户可以利用空闲任务执行一些后台工作,如系统监控、内存检查、低优先级的后台任务等。

  5. 系统启动后自动运行:一旦系统启动,空闲任务会一直存在并运行,它是系统任务调度的一部分,确保 CPU 不会闲置。

空闲任务钩子函数(Idle Hook Function)

空闲任务钩子函数是一个用户自定义的函数,它会在每次空闲任务运行时被调用。通过配置空闲任务钩子函数,可以在系统空闲时执行特定的操作。空闲任务钩子函数必须是快速且非阻塞的,以免影响系统的正常调度。

配置空闲任务钩子函数

要使用空闲任务钩子函数,需要在 FreeRTOSConfig.h 文件中定义以下宏:

#define configUSE_IDLE_HOOK 1

定义这个宏后,开发者需要实现一个 vApplicationIdleHook 函数,FreeRTOS 会在空闲任务运行时调用该函数。

void vApplicationIdleHook(void);

使用场景

  1. 系统监控:可以在空闲任务钩子函数中检查系统资源使用情况,如内存使用、CPU 使用率等,以便在系统空闲时进行资源管理和监控。

  2. 后台任务:可以在空闲任务钩子函数中执行一些低优先级的后台任务,如日志记录、数据采集等。

  3. 功耗管理:在空闲任务钩子函数中,可以调用进入低功耗模式的代码,从而在系统空闲时降低功耗。

  4. 内存管理:可以在空闲任务钩子函数中检查内存泄漏、回收内存等,以保持系统的稳定性和健壮性。

  5. 系统维护:可以在空闲任务钩子函数中执行系统维护任务,如清理缓存、更新状态等。

注意事项

  1. 非阻塞:空闲任务钩子函数必须是非阻塞的,因为它会在每次空闲任务运行时调用。如果钩子函数中包含阻塞代码,会影响系统的任务调度和性能。

  2. 快速执行:钩子函数的执行时间应该尽可能短,以免延迟其他任务的调度。

  3. 不可调用 FreeRTOS API:在空闲任务钩子函数中,不能调用可能导致任务切换的 FreeRTOS API,如阻塞函数 vTaskDelayxQueueReceive 等。

  4. 低优先级任务:钩子函数中的操作应尽量是低优先级的后台工作,不应包含关键任务或时间敏感的操作。
    果钩子函数中包含阻塞代码,会影响系统的任务调度和性能。

  5. 快速执行:钩子函数的执行时间应该尽可能短,以免延迟其他任务的调度。

  6. 不可调用 FreeRTOS API:在空闲任务钩子函数中,不能调用可能导致任务切换的 FreeRTOS API,如阻塞函数 vTaskDelayxQueueReceive 等。

  7. 低优先级任务:钩子函数中的操作应尽量是低优先级的后台工作,不应包含关键任务或时间敏感的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值