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=1 | configSUPPORT_STATIC_ALLOCATION=1 |
- 动态创建任务 :任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配。
- 静态创建任务 :任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。
3、任务控制块结构体成员介绍
答:
总结:
- 任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关。
- 每个任务都有属于自己的任务控制块,类似身份证。
4、什么是临界区保护
答:临界区保护,保护那些不想被打断的程序段,关闭freertos所管理的中断,中断无法打断,滴答中断和PendSV中断无法进行不能实现任务调度 。但只能关闭freertos规定的优先级的中断,溢出的优先级中断关闭无效
注意事项:
-
临界区应尽量简短
-
禁止嵌套深度超过configMAX_SYSCALL_INTERRUPT_PRIORITY
taskENTER_CRITICAL(); // 关闭可管理的中断
/* 临界区代码 */
taskEXIT_CRITICAL(); // 恢复中断
5、动态创建优点
答:动态创建使用起来相对简单。在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务 。
6、静态创建优点
答:静态创建可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理 。
7、创建任务时任务堆栈所存内容
答:
- 寄存器下PSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令。
- 寄存器PC被初始化为任务函数指针(任务A,即我们写的任务函数的地址),这样当某次任务切换后,任务A获得CPU控制权,任务函数(任务A)被出栈到PC寄存器,之后会执行任务A的代码。
- LR寄存器初始化为函数指针prvTaskExitError,这个函数是FreeRTOS提供的,是一个出错处理函数。
- 子函数的调用通过寄存器R0~R3传递参数,创建任务时,我们传入的参数被保存到R0中,用来向任务传递参数。
二、任务创建---动态方法(掌握)
1、动态任务创建函数
答:
函数参数:
函数返回值:
2、实现动态创建任务流程
答:用起来只需三步。
- 将FreeRTOSConfig.h文件中宏configSUPPORT_DYNAMIC_ALLOCATION配置为1。
- 定义函数入口参数。
- 编写任务函数。
动态任务创建函数创建的任务会立刻进入就绪态,由任务调度器调度运行。
void vTaskDemo(void *pvParams) {// 任务主体代码for(;;) {vTaskDelay(1000 / portTICK_PERIOD_MS);}
}void main() {xTaskCreate(vTaskDemo, "DemoTask", 512, NULL, 2, NULL);vTaskStartScheduler();
}
3、动态任务创建函数内部实现简述
答:
- 申请堆栈内存&任务控制块内存。
- TCB结构体(任务控制块)成员赋值。
- 添加新任务到就绪列表中。
- 堆内存结构:
+----------------+
| TCB控制块 |
+----------------+
| 任务栈空间 |
+----------------+
| 对齐填充 |
+----------------+
三、任务创建---静态方法(掌握)
1、静态任务创建函数
答:
函数参数:
函数返回值:
2、实现静态创建任务流程
答:用起来只需五步。
- 将FreeRTOSConfig.h文件中宏configSUPPORT_STATIC_ALLOCATION配置为1。
- 定义空闲任务&定时器任务的任务堆栈以及TCB。
- 实现两个接口函数(vAppLicationGetldleTaskMemory() 空闲任务接口函数和vApplicationGetTimerTaskMemory()定时器任务接口函数)。
- 定义函数入口参数。
- 编写任务函数。
静态任务创建函数创建的任务会立刻进入就绪态,由任务调度器调度运行。
3、静态任务创建函数内部实现简述
答:
- TCB结构体成员赋值。
- 添加新任务到就绪列表中。
四、任务删除
1、任务删除函数
答:
任务删除函数用于删除已经被创建的任务,被删除的任务将从就绪任务列表、阻塞任务列表、挂起任务列表和事件列表中移除。
注意:
- 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。
- 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存空间,必须用户在任务被删除前提前释放,否则将会导致内存泄漏。
2、删除任务流程
答:用起来只需两步。
- 使用删除任务函数,将FreeRTOSConfig.h文件中宏INCLUDE_vTaskDelete配置为1。
- 入口参数输入需要删除的任务句柄(NULL代表删除本身)。
3、删除任务函数内部实现简述
答:
-
获取所要删除的任务控制块 --- 通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。
-
将被删除任务移除所在列表 --- 将该任务所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。
-
判断所需要删除的任务
-
删除任务自身,需要先添加到等待删除列表,内存释放将在空闲任务执行。
-
删除其他任务,释放内存,任务数量。
-
五 内部机制解析
任务创建时序
-
内存分配(动态)
-
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);
}
-
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. 任务同步应用
-
// 生产者-消费者模型 TaskHandle_t xConsumerHandle;void vProducerTask(void *pvParam) {while(1) {// 生产数据...vTaskResume(xConsumerHandle); // 唤醒消费者vTaskSuspend(NULL); // 挂起自身等待下一周期} }void vConsumerTask(void *pvParam) {while(1) {vTaskSuspend(NULL); // 等待生产者唤醒// 消费数据...} }
-
三、状态转换机制
1. 状态迁移图
图表
代码