当前位置: 首页 > news >正文

FreeRTOS-任务的创建删除,挂起与恢复

一、任务创建和删除API函数(熟悉)

1. 任务创建与删除的本质:

  • 创建:分配资源并注册到调度系统

  • 删除:释放资源并从调度系统中注销

 用户在进行任务创建和删除的时侯可以调用FreeRTOS的API函数。

// 典型API调用示例
xTaskCreate(taskFunction, "Task1", 1024, NULL, 1, &xHandle);
vTaskDelete(xHandle);BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */const configSTACK_DEPTH_TYPE usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask )

2、任务动态创建和静态创建的区别

. 动态与静态创建对比

特性动态创建静态创建
内存管理系统自动分配用户预分配
失败处理返回错误码需自行保证内存充足
适用场景常规应用开发内存敏感/实时性要求高的系统
配置要求configSUPPORT_DYNAMIC_ALLOCATION=1configSUPPORT_STATIC_ALLOCATION=1
  • 动态创建任务 :任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配。
  • 静态创建任务 :任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。

3、任务控制块结构体成员介绍

答:

总结:

  1. 任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关。
  2. 每个任务都有属于自己的任务控制块,类似身份证。

4、什么是临界区保护

答:临界区保护,保护那些不想被打断的程序段,关闭freertos所管理的中断,中断无法打断,滴答中断和PendSV中断无法进行不能实现任务调度 。但只能关闭freertos规定的优先级的中断,溢出的优先级中断关闭无效

注意事项

  1. 临界区应尽量简短

  2. 禁止嵌套深度超过configMAX_SYSCALL_INTERRUPT_PRIORITY

taskENTER_CRITICAL();  // 关闭可管理的中断
/* 临界区代码 */
taskEXIT_CRITICAL();   // 恢复中断

5、动态创建优点

答:动态创建使用起来相对简单。在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务 。

6、静态创建优点

答:静态创建可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理 。

7、创建任务时任务堆栈所存内容

答:

  1. 寄存器下PSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令。
  2. 寄存器PC被初始化为任务函数指针(任务A,即我们写的任务函数的地址),这样当某次任务切换后,任务A获得CPU控制权,任务函数(任务A)被出栈到PC寄存器,之后会执行任务A的代码。
  3. LR寄存器初始化为函数指针prvTaskExitError,这个函数是FreeRTOS提供的,是一个出错处理函数。
  4. 子函数的调用通过寄存器R0~R3传递参数,创建任务时,我们传入的参数被保存到R0中,用来向任务传递参数。

二、任务创建---动态方法(掌握)

1、动态任务创建函数

答:

函数参数:

函数返回值:

2、实现动态创建任务流程

答:用起来只需三步。

  1. 将FreeRTOSConfig.h文件中宏configSUPPORT_DYNAMIC_ALLOCATION配置为1。
  2. 定义函数入口参数。
  3. 编写任务函数。

动态任务创建函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

void vTaskDemo(void *pvParams) {// 任务主体代码for(;;) {vTaskDelay(1000 / portTICK_PERIOD_MS);}
}void main() {xTaskCreate(vTaskDemo, "DemoTask", 512, NULL, 2, NULL);vTaskStartScheduler();
}

3、动态任务创建函数内部实现简述

答:

  1. 申请堆栈内存&任务控制块内存。
  2. TCB结构体(任务控制块)成员赋值。
  3. 添加新任务到就绪列表中。
  4. 堆内存结构:
    +----------------+
    | TCB控制块      |
    +----------------+
    | 任务栈空间      |
    +----------------+
    | 对齐填充        |
    +----------------+

三、任务创建---静态方法(掌握)

1、静态任务创建函数

答:

函数参数:

函数返回值:

2、实现静态创建任务流程

答:用起来只需五步。

  1. 将FreeRTOSConfig.h文件中宏configSUPPORT_STATIC_ALLOCATION配置为1。
  2. 定义空闲任务&定时器任务的任务堆栈以及TCB。
  3. 实现两个接口函数(vAppLicationGetldleTaskMemory() 空闲任务接口函数和vApplicationGetTimerTaskMemory()定时器任务接口函数)。
  4. 定义函数入口参数。
  5. 编写任务函数。

静态任务创建函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

3、静态任务创建函数内部实现简述

答:

  1. TCB结构体成员赋值。
  2. 添加新任务到就绪列表中。

四、任务删除

1、任务删除函数

答:

任务删除函数用于删除已经被创建的任务,被删除的任务将从就绪任务列表、阻塞任务列表、挂起任务列表和事件列表中移除。

注意:

  1. 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。
  2. 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存空间,必须用户在任务被删除前提前释放,否则将会导致内存泄漏。

2、删除任务流程

答:用起来只需两步。

  1. 使用删除任务函数,将FreeRTOSConfig.h文件中宏INCLUDE_vTaskDelete配置为1。
  2. 入口参数输入需要删除的任务句柄(NULL代表删除本身)。

3、删除任务函数内部实现简述

答:

  1. 获取所要删除的任务控制块 --- 通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。

  2. 将被删除任务移除所在列表 --- 将该任务所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。

  3. 判断所需要删除的任务

    • 删除任务自身,需要先添加到等待删除列表,内存释放将在空闲任务执行。

    • 删除其他任务,释放内存,任务数量。

    • 五 内部机制解析

      任务创建时序

    • 内存分配(动态)

    • TCB初始化

    • 任务删除流程

    • 从所有列表中移除

    • 释放内核资源

    • 更新调度状态

    • 触发上下文切换(如果需要)

    • 堆栈初始化

    • 优先级设置

    • 加入就绪列表

    • 一、任务挂起和任务恢复介绍(熟悉)

      1、任务的挂起与恢复的API函数

      答:

    • 挂起:挂起任务类似暂停,可恢复;删除任务,无法恢复,类似“人死两清”。
    • 恢复:恢复被挂起的任务。
    • “FromISR”:带有FromISR后缀是在中断函数中专用的API函数。

    • 二、任务挂起(熟悉)

      1、任务挂起函数介绍

      答:

      任务挂起函数用于挂起任务,使用时需要将将FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend配置为1。

      无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复。

      注意:当传入的参数是NULL,则代表挂起任务自身(当前正在运行的任务)。

      2、任务挂起函数内部实现

      答:

    • 获取所要挂起任务的控制块。(根据任务句柄获取任务控制块,如果任务句柄为NULL,表示挂起任务本身。)
    • 移除所在列表。(将要挂起的任务从相应的状态列表和事件列表中移除。)
    • 插入挂起任务列表。(将待挂起任务的任务状态列表项插入到挂起状态列表末尾。)
    • 判断任务调度器是否运行。(判断任务调度器是否运行,在运行,更新下一次阻塞时间,防止被挂起任务为下一个阻塞超时任务。)
    • 判断待挂起任务是否为当前任务。(如果挂起的是任务自身,且调度器正在运行,需要进行一次任务切换;调度器没有运行,判断挂起任务数是否等于任务总数,是:当前控制块赋值为NULL,否:寻找下一个最高优先级任务。)

    • 三、任务恢复(熟悉)

      1、任务恢复函数介绍(任务中恢复)

      答:

      使用该函数时需要将FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend配置为1。

      注意:任务无论被vTaskSuspend()挂起多少次,只需在任务中调用vTaskResume()恢复一次就能继续运行,且被恢复的任务会进入就绪态。

      2、任务恢复函数(任务中恢复)内部实现

      答:

    • 恢复任务不能是正在运行任务。
    • 判断任务是否子啊挂起列表中。(是:就会将该任务在挂起列表中移除,将该任务添加到就绪列表中。)
    • 判断恢复任务优先级。(判断恢复的任务优先级是否大于当前正在运行的任务,是的话,执行任务切换。)
    • 3、任务恢复函数介绍(中断中恢复)

      答:

      使用该函数注意要将FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend和INCLUDE_xTaskResumeFromISR配置为1。

      该函数专用于中断服务函数中,用于解挂被挂起任务。

      注意:中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级。

      4、任务恢复函数(中断中恢复)内部实现

    • 关闭freertos可管理中断,防止被其他的中断打断,并返回关闭前basepri寄存器的值。
    • 判断是否有挂起任务。
    • 将前面保存的basepri的值,恢复回来。
    • 返回xYieldRequired的值 用于决定是否需要进行任务切换。
    • 从所有状态列表(就绪/阻塞/事件)中移除任务

    • 将任务状态标记为 eSuspended

    • 检查任务是否处于挂起态

    • 将任务加入就绪列表

    • 若恢复任务的优先级高于当前任务,触发上下文切换

    • 如果挂起的是当前运行任务,立即触发上下文切换

七、实战代码示例

1. 基本挂起/恢复

TaskHandle_t xWorkerHandle;void vWorkerTask(void *pvParam) {while(1) {// 任务主体...}
}void vControlTask(void *pvParam) {// 挂起工作线程vTaskSuspend(xWorkerHandle);vTaskDelay(2000 / portTICK_PERIOD_MS);// 恢复工作线程vTaskResume(xWorkerHandle);
}
  1. 2. 中断恢复示例

    TaskHandle_t xISRTaskHandle;void vButtonPressISR(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;xHigherPriorityTaskWoken = xTaskResumeFromISR(xISRTaskHandle);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }void vISRHandlerTask(void *pvParam) {while(1) {vTaskSuspend(NULL);  // 等待中断唤醒// 处理中断事件...}
    }

    3. 任务同步应用

  2. // 生产者-消费者模型
    TaskHandle_t xConsumerHandle;void vProducerTask(void *pvParam) {while(1) {// 生产数据...vTaskResume(xConsumerHandle);  // 唤醒消费者vTaskSuspend(NULL);            // 挂起自身等待下一周期}
    }void vConsumerTask(void *pvParam) {while(1) {vTaskSuspend(NULL);  // 等待生产者唤醒// 消费数据...}
    }

  3. 三、状态转换机制

    1. 状态迁移图

    图表

    代码

相关文章:

  • JavaFX深度实践:从零构建高级打地鼠游戏(含多物品与反馈机制)
  • Springboot 集成 RBAC 模型实战指南
  • C++IO流
  • Electron使用WebAssembly实现CRC-32 原理校验
  • 【项目】基于MCP+Tabelstore架构实现知识库答疑系统
  • 测试OMS(订单管理系统)时,对Elasticsearch(ES)数据和算法数据进行测试(如何测试几百万条数据)
  • UDP协议理解
  • 【(保姆级教程)Ubuntu24.10下部署Dify】
  • 【C语言】动态内存的常见错误
  • JavaFX 实战:从零打造一个功能丰富的英文“刽子手”(Hangman)游戏
  • NLP高频面试题(五十一)——LSTM详解
  • 玩转Docker | 使用Docker部署DashMachine个人书签工具
  • 深度学习3.6 softmax回归的从零开始实现
  • 模拟实现strncat、qsort、atoi
  • 低光环境下双目云台摄像头监控性能解析
  • Element UI、Element Plus 里的表单验证的required必填的属性不能动态响应?
  • 题解:[ABC385F] Visible Buildings
  • GNOME桌面隐藏回收站和分区
  • 赛灵思 XC7K325T-2FFG900I FPGA Xilinx Kintex‑7
  • 基于SpringBoot的中华诗词文化分享平台-项目分享
  • 4月人民币对美元即期汇率微跌,今年以来升值0.48%
  • 朝鲜海军新型驱逐舰进行首次武器系统测试
  • 移动互联网未成年人模式正式发布
  • 南京航空航天大学启动扁平化改革:管理岗规模控制在20%,不再统一设科级机构
  • 俄宣布停火三天,外交部:希望各方继续通过对话谈判解决危机
  • 来伊份一季度净利减少近八成,今年集中精力帮助加盟商成功