目录
一、简介
在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面, CPU按照顺序执行代码。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务(Task)。在FreeRTOS中,任务是指独立的、并发执行的功能模块,任务是最基本的执行单元,类似于一个独立的线程。操作系统通过任务调度器对多个任务进行调度。
二、任务栈
任务栈(Task Stack)是每个任务独立使用的内存空间,主要用于存储任务的局部变量、函数调用的返回地址、寄存器内容等。当任务发生上下文切换时,任务的运行状态会保存在其栈中,以便任务恢复时能够继续从切换点继续执行。
在创建任务时,开发者需要指定任务的栈大小。栈的大小通常是以字(words)为单位的。在32位系统上,每个字等于4字节,而在16位系统上,每个字等于2字节。栈的大小必须根据任务的复杂性、局部变量的数量、函数调用的深度来合理设置。
如果栈大小设置得太小,任务执行过程中可能会发生栈溢出,导致系统崩溃。因此,FreeRTOS提供了两种不同的栈溢出检测机制,帮助开发者检测和处理栈溢出问题,可以通过在FreeRTOS的配置文件中启用:
- 模式1:每次任务切换时,系统会检查栈顶的值,确保栈没有超出其分配的内存区域。
- 模式2:除了模式1的检查外,模式2会进一步检测栈的起始区域是否被破坏。模式2的检测更加全面,但也会带来更多的性能开销。
三、任务控制块
任务控制块(TCB)是FreeRTOS用来管理任务的核心数据结构。每个任务在系统中都对应一个TCB,TCB包含了任务调度和管理所需的所有信息,系统通过TCB来记录和管理每个任务的状态、优先级、栈指针等信息。
四、任务状态
每个任务在系统中都有不同的状态,任务的状态决定了它是否可以被调度执行。FreeRTOS中的任务状态通常包括以下几种:
1.就绪态(Ready)
当任务处于就绪态时,它已经可以执行,但调度器可能由于其他更高优先级的任务正在执行而暂时没有给它分配CPU时间。处于就绪态的任务会等待调度器将其调度执行。
2.运行态(Running)
当前占用CPU执行的任务称为运行态。当调度器选择一个任务运行时,它会从就绪态切换到运行态。
3.阻塞态(Blocked)
任务在等待某个事件(如等待信号量、队列消息或时间延迟)时会进入阻塞态。阻塞任务在其等待的事件发生前不会被调度执行。一旦事件发生,任务会从阻塞态转移到就绪态。
4.挂起态(Suspended)
挂起态的任务是由用户或系统主动挂起的任务,处于这种状态的任务不会被调度器调度执行。与阻塞态不同,挂起态的任务无法自动恢复,必须通过显式调用vTaskResume()
来恢复任务。
五、任务调度
FreeRTOS是一个基于优先级的抢占式实时操作系统。它的调度器根据任务的优先级来决定当前哪个任务应该运行。调度器的核心职责是从任务的就绪列表中选择一个优先级最高的任务来执行。 FreeRTOS的任务调度机制可以分为以下几种策略:
- 抢占式调度:在抢占式调度中,高优先级任务可以随时抢占低优先级任务的执行。当一个新的高优先级任务进入就绪状态时,调度器会立即暂停低优先级任务,转而运行高优先级任务。
- 协作式调度:协作式调度要求任务主动放弃CPU(通过调用
taskYIELD()
函数)。调度器不会自动切换任务,只有当任务主动释放控制权时,才会进行任务切换。 - 时间片调度:当多个任务具有相同优先级时,调度器会按照时间片轮转的方式调度任务执行。时间片的长度由系统的时钟中断频率决定。
五、任务间的通信
FreeRTOS为任务间的通信和同步提供了多种机制:
- 消息队列:消息队列是FreeRTOS中任务间通信的主要机制,任务可以通过队列发送或接收数据。详细请看:消息队列
- 信号量:信号量用于任务间的同步,特别是用于资源的访问控制。详细请看:信号量
- 事件标志组:事件标志组允许任务等待多个事件发生,是任务间同步的另一种机制。详细请看:事件标志组
- 任务通知:任务通知是一种轻量级的任务间通信方式,它使用较少的内存和时间开销。详细请看:任务通知
六、相关API
1.静态创建任务
函数原型:
TaskHandle_t xTaskCreateStatic(
(TaskFunction_t ) pxTaskCode, //任务函数
(const char* ) pcName, //任务名称
(uint32_t ) ulStackDepth, //任务堆栈大小
(void* ) pvParameters, //传递给任务函数的参数
(UBaseType_t ) uxPriority, //任务优先级
(StackType_t* ) puxStackBuffer, //任务堆栈
(StaticTask_t* ) pxTaskBuffer //任务控制块
);
任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建。动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放。静态创建时,任务控制块和栈的内存需要事先定义好,是静态的内存,任务删除时,内存不能释放。(侧重介绍动态创建,静态创建用的少)
2.动态创建任务
函数原型:
BaseType_t xTaskCreate(
(TaskFunction_t )pxTaskCode, //任务函数
(const char* )pcName, //任务名称
(uint16_t )ulStackDepth, //任务堆栈大小
(void* )pvParameters, //传递给任务函数的参数
(UBaseType_t )uxPriority, //任务优先级
(TaskHandle_t* )pxCreatedTask //任务句柄
);
使用示例:
//开始任务的任务函数
void start_task(void *pvParameters)
{
//函数主体
}
//任务堆栈大小
#define START_STK_SIZE 128
//任务优先级
#define START_TASK_PRIO 1
//任务句柄
TaskHandle_t StartTask_Handler;
//创建开始任务
xTaskCreate(
(TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler //任务句柄
);
3.挂起任务
该函数可以使指定的任务进入挂起态,被挂起的任务不会参与调度,它相对于调度器而言是不可见的,除非它从挂起态中解除。 void vTaskSuspendAll(void)可以挂起所有任务。
4. 恢复任务
该函数可以使指定的挂起态任务重新进入就绪态,即解除挂起。BaseType_t xTaskResumeAll(void)可以解除所有挂起态任务,与void vTaskSuspendAll(void)相对。
5.删除任务
void vTaskDelete(TaskHandle_t xTaskToDelete)用于将任务从所有就绪,阻塞,挂起和事件列表中删除,无返回值,参数为任务句柄。
6.任务相对延时
void vTaskDelay( const TickType_t xTicksToDelay ) ,任务调用该函数后,任务将进入阻塞状态,进入阻塞态的任务将让出 CPU 资源。无返回值,参数为系统节拍数。
7.任务绝对延时
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )。
8.启动任务调度器
void vTaskStartScheduler( void ) ,创建好任务后,调用该函数,系统即可开始调度任务,执行高优先级任务。
标签:优先级,函数,FreeRTOS,void,调度,任务 From: https://blog.csdn.net/han2205277149/article/details/143086847