目录
- 1、任务相关基本概念
- 1.1 任务函数原型
- 1.2 任务句柄TaskHandle_t 及任务控制块TCB_t
- 1.3 任务状态
- 1.4 优先级(Task Priorities)
- 2 创建任务
- 2.1 xTaskCreate
- 2.2 xTaskCreateStatic()
- 3 延迟函数使任务进入阻塞状态
- vTaskDelay()函数
- vTaskDelayUntil()函数
- 4 优先级操作函数
- 4.1 vTaskPrioritySet() 函数
- 4.2 uxTaskPriorityGet() 函数
- 5 任务删除函数
- 6 总结
1、任务相关基本概念
1.1 任务函数原型
在上一章里(原文链接),已经把任务的定义说明了
FreeRTOS驱动的执行一定功能的对象叫任务”(task),这些任务就是由C语言编写的一段代码或叫函数。这段函数一般具有一个无限循环,不能带有return语句。因为如果return了,那么任务其实就返回了,结束了。该任务也消亡了。任务有很多相关的属性,其中比较重要的是任务的状态(就绪态ready , 运行态running , 阻塞态blocked,挂起态suspend)
这只是理论的描述,具体到操作上,任务是一个函数。该函数的原型为
void ATaskFunction( void * vParameters );
FreeRTOS里给这个原理定义了一个类型TaskFunction_t ,定义如下:
也就是所有做为任务的函数必须定义成如上格式。其无返回值,函数名自取,参数为void*一个无类型指针。
实例:任务函数的实例:
void myTask(void * p){
char * str = "myTask is runnint.";
while(1){
printf(str);
}
vTaskDelete(NULL);
}
1.2 任务句柄TaskHandle_t 及任务控制块TCB_t
该句柄的定义链如下:
首先在task.h中定义了TaskHandle_t
这里可以看出实际TaskHandle_t是“任务控制块”这个结构体类型指针的别名。原文对任务控制块的的描述如下:
任务控制块。为每个任务分配任务控制块(TCB),并存储任务状态信息,包括指向任务上下文的指针(任务的运行时环境,包括寄存器值)
任务控制块的原始定义在task.h头文件中:
typedef struct tskTaskControlBlock
{
/* 栈顶<元素>指针,注意与 pxEndOfStack 的区别。 必须是结构体的第一个成员. */
volatile StackType_t * pxTopOfStack;
/* MPU 模块设置,必须是结构体的第二个成员 */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings;
#endif
ListItem_t xStateListItem; /* 列表项指示任务当前的状态 (Ready, Blocked, Suspended ). */
ListItem_t xEventListItem; /* 用于在事件列表中. */
UBaseType_t uxPriority; /* 任务优先级(0为优先级最低). */
StackType_t * pxStack; /* 栈底指针,指向栈开始处. */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名 */
/* 如果栈向上生长,则定义栈顶指针 */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t * pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /* 保存临界区的栈深度. 在函数vTaskEnterCritical() 和 vTaskExitCritical()中会维护这个值*/
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /*TCB的编号,每次增加一个task,TCB的编号就会增加. */
UBaseType_t uxTaskNumber; /*task的编号,其值和uxTCBNumber相等,可用于调试追踪. */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /*上一次分配给该任务的优先级,用于优先级继承机制. */
UBaseType_t uxMutexesHeld; /* 持有的信号量 */
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
/* 存储一些私有的变量 */
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
/* task 运行的总时间 */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter;
#endif
/* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html for additional information. */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
/* 任务通知相关变量 */
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
/* FreeRTOS.h 中有关于 tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE 的详细注释。主要的作用是决定task创建的资源是动态还是静态分配内存. */
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated; /* 标记stack 和 TCB分配方式,在删除任务时,根据此标志来对应释放内存 */
#endif
/* delay 终止标志 */
#if ( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
/* error number*/
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
} tskTCB;
旧的tskTCB名称保持在上面,然后再次定义为下面的新TCB_t名称,以启用旧的内核感知调试器。
以上可以清楚的知道,任务控制块也好,任务句柄也好,其实就是这么一个数据结构,内部存储了与该任务运行相关的所有必要的各种环境数据,状态数据,栈数据,资源数据,调度相关数据等。与任务一一对应起来。一个任务控制块对应一个具体的任务。FreeRTOS系统中所有对任务的操作,调度,控制,管理等行为都必须通过任务控制块来完成。
1.3 任务状态
CPU对FreeRTOS来说就是一个资源,一个用于执行任务的资源。因此当一个CPU上要执行多个任务时,必然就要解决任务的切换。这个时间段(比如1个毫秒)里要执行任务A,下一个时间段要执行任务B,执行任务B时任务A就不能工作了。因此对FreeRTOS从控制的角度而言,每个时刻,任务A与任务B就会处理不同的状态。比如运行态与非运行态。当然,FreeRTOS面对的任务运行的状态还会受到任务本身实际情况的影响,比如,有的任务因为某些资源没就绪,就会停在那里等待,某些任务可能在一个特定条件下不需要运行了,会处理持起状态而不参与到系统的调度中从而节约CPU时间。为能全面的反应当下任务的状态,同时也要精确控制,FreeRTOS对任务定义了以下四种状态:
运行态runnint :正在占用CPU时间的任务。
就绪态ready:处于未运行状态但未被阻止或挂起的任务称为处于就绪状态。它们能够运行,因此“准备”运行,但当前未处于运行状态。
阻塞态blocked :等待事件的任务被称为处于“阻塞”状态。任务进入阻塞状态等待两种不同类型的事件:一个是时间(时间相关)事件,例如,一个任务可能会用xTaskDelay()函数使自已进入 Blocked 状态以等待 10 毫秒通过。另一个是同步事件——事件源自另一个任务或中断。例如,任务可能进入阻塞状态以等待数据到达队列。同步事件涵盖了广泛的事件类型。
暂停态suspended:处于暂停(或叫挂 起)状态的任务对调度程序不可用,节约了系统时间。进入Suspended状态的唯一方法是调用vTaskSuspend()API函数,离开的唯一的方法是调用vTaskResume()或xTaskResumeFromISR()API功能。
任务状态在实际调度时,是以链表的形态存在的。FreeRTOS会为每个状态维护一个对应状态链表,当某个任务处于什么态时,他的TCB就会被调入对应的状态链表中。如,一个任务现在处在阻塞态,那它的TCB就会被写入阻塞状态链表。因此,一个任务在整个生命周期中所处的状态会随着程序的运行而不断被挂入不同的状态链表,下面是状态循环图:
1.4 优先级(Task Priorities)
任务优先级用于确定任务被调度时的优先顺序。因此在任务创建时,开发者必须以参数的形式指定这个任务的优先级,优先级也可以在任务执行过程中被动态的改变。
FreeRTOS用宏configMAX_PRIORITIES 来控制可以设置的最大优先级。这个宏在 FreeRTOSConfig.h中进行设置。
FreeRTOS调度器确保处于就绪或正在运行状态的任务总是优先于同样处于就绪状态的较低优先级的任务而获得处理器(CPU)时间。换句话说,处于运行状态的任务始终是能够运行的最高优先级任务。
任何数量的任务都可以共享相同的优先级。如果未定义configUSE_TIME_SLICING,或者如果configUSE_TIME_SLICING设置为1,则同等优先级的就绪状态任务将使用时间分段循环调度方案共享可用的处理时间。
2 创建任务
FreeRTOS提供的任务创建函数通常有两个,一个是xTaskCreate(),另一个是xTaskCreateStatic()。这两个函数是相对通用的函数,但对于esp32这种双核cpu的平台。ESP-IDF提供了两个替代函数。下面一一解释。
2.1 xTaskCreate
函数的声明
#include “FreeRTOS.h”
#include “task.h”
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
参数
参数 | 注释 |
---|---|
TaskFunction_t pvTaskCode | 这个参数就是你要创建的任务的原型函数指针。在上面1.1节里有详细描述。 |
const char * pcName | 一个任务的描述名称。这是主要主用是使调试更方便,但也可被用于调用xTaskGetHandle()函数来获得任务句柄。应用程序预定义常量configMAX_TASK_NAME_LEN定义了名称中包括空中止符的字母的最大长度 |
uint16_t usStackDepth | 每个任务都有自已的唯一的栈,当任务被创建时由内核分配给任务。usStackDepth的值告诉内核分配多大的栈。这个值指定的栈能容下的字的数量,不是字节数。例如,在一个4字节的栈宽度的平台,如果usStackDepth被设为100,那么栈的空间将被分配为100*4字节。栈的深度乘以栈的宽度不能大于变量size_t类型的最大取值。用于空闲任务的栈的尺寸由程序预定义常量configMINIMAL_STACK_SIZE来定义。在这个由所选择的MCU所提供的demo中,被分配给这个常量的值,是在这个平台中被推荐给任务的最小值。如果你的任务使用很大的栈空间,那么你必须指定更大的值。 |
void * pvParameters | 任务函数接受一个指向void的指针参数。被分配给pvParameters的值将传递给任务。这个参数有一个指向void的类型,允许任务参数更有效率,并间接的通过casting,接收任意类型参数。举例,通过在任务创建这个点时把整形casting到void指针,则整形能被传递进任务函数 |
UBaseType_t uxPriority | 定义任务执行的优先级。优先级能被分配为0(最低优先级)至configMAX_PRIORITIES-1(最高优先级)。configMAX_PRIORITIES是一个用户定义的常量。如果configUSE_PORT_OPTIMISED_TASK_SELECTION被设为0那么没有可用的优先级上限(以外的所使用的数据类型和微控制器中可用的RAM的限制)但是建议使用最低所需要的优先级,以避免浪费内存。传递一个uxPriority值大于(configMAX_PRIORITIES - 1)将导致分配给任务的优先级被默默地设置为最大的合法的值。值越小,优先级也越小。 |
TaskHandle_t * pxCreatedTask | pxCreatedTask能被和于传递出一个被创建的任务的句柄。然后,该句柄可用于在API调用中引用该任务例如,修改任务优先级或删除任务。如果你的应用程序没有使用任务句柄,那么pxCreatedTask可被设置为NULL |
有两种可能的返回值
返回值 | 解释 |
---|---|
pdPASS | 这表示任务已成功创建。 |
pdFAIL | 这表明任务尚未创建,因为FreeRTOS没有足够的堆内存来分配足够的RAM来保存任务数据结构和堆栈。 |
注意:configSUPPORT_DYNAMIC_ALLOCATION必须设置为1,才能使用这个函数
实列
typedef struct A_STRUCT
{
char cStructMember1;
char cStructMember2;
} xStruct; //定义了这个结构体类型,传递胡数给任务。
xStruct xParameter = { 1, 2 }; //定义具体的结构体变量,作为给任务传递的参数
void vTaskCode( void * pvParameters ) //定义任务的原型函数,这个函数名将做为xTaskCreate()的第一个参数,详见下面代码
{
xStruct *pxParameters; //定义的这个结构体指针,用于接收参数传入的指针
pxParameters = ( xStruct * ) pvParameters; //把参数的void 类型指针强转成xStruct类型指针
if( pxParameters->cStructMember1 != 1 )
{
/* 这里进行一些处理,比如打印传入的参数(略). */
}
for( ;; ) //进入无限循环,进行任务的具体处理
{
/* Task code goes here. */
}
}
void main( void )
{
TaskHandle_t xHandle;
/* 接下来创建任务. */
xTaskCreate(
vTaskCode, /* 指向刚才定义的函数的指针,其实就是函数名,做为这个任务的具体实现。 */
"Demo task", /* 给任务起的文本名*/
STACK_SIZE, /* 定义的栈的大小,以字为单位(4字节) 比如1024个字*/
(void*) &xParameter, /* 给任务的参数的引用,这里用void*强转是为了防止编译器产生烦人的告警 */
TASK_PRIORITY, /* 指定创建的任务的优先级. */
&xHandle /* 创建的任务的句柄会存入xHandle*/
)
xTaskStartSchadule(); //启动调度器
}
2.2 xTaskCreateStatic()
每个任务都需要RAM,用于保存任务状态(任务控制块或TCB),并由任务用作其堆栈。如果使用xTaskCreate()创建任务,则所需的RAM将自动从FreeRTOS堆中分配。如果使用xTaskCreateStatic()创建任务,则RAM由应用程序编写器提供,这将导致两个额外的函数参数,但允许在编译时静态分配RAM。
新创建的任务最初处于就绪状态,但如果没有更高优先级的任务可以运行,则会立即变为运行状态任务。
可以在调度程序启动之前和之后创建任务。
#include “FreeRTOS.h”
#include “task.h”
TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
const char * const pcName,
uint32_t ulStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer );
参数 | 注释 |
---|---|
StackType_t * const puxStackBuffer | 必须指向至少具有ulStackDepth索引的StackType_t变量数组。数组将用作创建的任务的堆栈,因此必须是持久的(不在函数创建的堆栈框架中声明,也不在应用程序执行时可以合法覆盖的任何其他内存中声明)。 |
StaticTask_t * const pxTaskBuffer | 必须指向StaticTask_t类型的变量。该变量将用于保存创建的任务的数据结构(TCB),因此它必须是持久的(不在函数创建的堆栈帧中声明,也不在应用程序执行时可以合法覆盖的任何其他内存中声明)。 |
返回值
返回值 | 解释 |
---|---|
NULL | 无法创建任务,因为puxStackBuffer或pxTaskBuffer为NULL。 |
任何其它值 | 如果返回非NULL值,则创建了任务,返回的值是创建的任务的句柄。 |
注意:在FreeRTOSConfig中,configSUPPORT_STATIC_ALLOCATION必须设置为1。h表示此功能可用。
#define STACK_SIZE 200 //标注做为堆栈的缓冲区大小。注意:这里单位是StackType_t大小,如果是32位,设置为100,那么将分配400字节(100*32位)。
StaticTask_t xTaskBuffer; //这个结构体变量用于存储创建的任务的TCB
/* Buffer that the task being created will use as its stack. Note this is an array of
StackType_t variables. The size of StackType_t is dependent on the RTOS port. */
StackType_t xStack[ STACK_SIZE ]; //具体定义栈数组,StackType_t的大小根RTOS port相关。即不同平台或架构可能不同。
void vTaskCode( void * pvParameters ) //任务的具体实现
{
configASSERT( ( uint32_t ) pvParameters == 1UL ); //就是比较一下,传入的参数是否为1
for( ;; )
{
/* 具体的处理代码 */
}
}
void main( void )
{
TaskHandle_t xHandle = NULL;
/* 创建任务,并以静态方式分配任务栈 */
xHandle = xTaskCreateStatic(
vTaskCode, /* 任务的原型函数名 */
"NAME", /* 任务的文本名 */
STACK_SIZE, /* xStack数组的元素大小. */
( void * ) 1, /* 传送1给任务 */
tskIDLE_PRIORITY, /* 任务的优先级 */
xStack, /* 做为任务栈的数组大小 */
&xTaskBuffer ); /* 存储任务的TCB,这个数据和xTaskCreate()不一样,不是存在栈里的,必须提前静态分配 */
// puxStackBuffer和pxTaskBuffer不为NULL,因此任务将被创建,xHandle将是任务的句柄。
xTaskStartSchadule();
vTaskSuspend( xHandle ); //使用句柄挂起任务。
}
3 延迟函数使任务进入阻塞状态
为什么不用空循环:在一般的开发中,为了延迟一段时间,都是使用空循环非常粗略地去地轮询一个递增的循环计数器,直到它达到一个固定值后循环结束。这种方法的缺点是很明显的,就是无效地占用了CPU的运行时间。对多任务操作系统而言,这种浪费是不受控以及不允许的。因此在FreeRTOS中,使用vTaskDelay()来替代这个空循环,并使任务进入一个阻塞状态,这样这个时间段里的CPU时间可以被FreeRTOS用来调度给其它任务使用。这样就极大地解决了之前说的这个浪费行为。
从这里也要以看出阻塞的任务是不占用CPU的。
vTaskDelay()函数
声明
void vTaskDelay( TickType_t xTicksToDelay );
参数:
参数 | 解释 |
---|---|
TickType_t xTicksToDelay | xTicksToDelay指的是任务在转换回就绪状态之前,任务将保持在“阻止”状态的tick中断数。例如,如果一个任务在当下的tick count为10000时调用了vTaskDelay(100),那么它将立即进入阻止状态,并保持在阻止状态,直到tick count 达到10100。宏pdMS_TO_TICKS()可用于将以毫秒为单位指定的时间转换为以刻度为单位的时间。例如,调用vTaskDelay(pdMS_TO_TICKS(100))将导致调用任务在100毫秒内保持“阻止”状态。 |
实例
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
const TickType_t xDelay250ms = pdMS_TO_TICKS( 250 );
/* 下面语句把一个传入的void指针强转为一个char指针 */
pcTaskName = ( char * ) pvParameters;
/* 具体任务的处理内容放在一个无限循环中 */
for( ;; )
{
/* 这个函数的意思就是输出一个变量的内容 */
vPrintString( pcTaskName );
vTaskDelay( xDelay250ms ); //调用这个函数,使任务进入阻塞时间一段时间。参数是以tick中断次数做为计时单位,常用pdMS_TO_TICKS()宏将毫秒转换成tick中断次数。
} }
函数的图示
vTaskDelayUntil()函数
vTaskDelayUntil() 类似于 vTaskDelay()。vTaskDelay() 参数指定了在调用 vTaskDelay()时开始的延迟时间。任务保持阻塞状态的时间长度由 vTaskDelay() 参数指定,状态的时间与调用 vTaskDelay() 的时间相关。该延迟时间是不计算任务的执行时间的。
相反, vTaskDelayUntil() 是在需要固定执行周期(您希望任务时间加上延迟时间的总时间是以固定频率定期执行)时应使用的 API 函数。
声明
void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );
参数
参数 | 解释 |
---|---|
TickType_t * pxPreviousWakeTime | 这个参数的命名是假设 vTaskDelayUntil() 被用来实现一个定期和固定频率执行的任务。在这种情况下,pxPreviousWakeTime 保存任务上次离开阻塞状态(被“唤醒”)的时间。该时间用作参考点来计算任务下一次应该离开阻塞状态的时间。pxPreviousWakeTime 指向的变量在vTaskDelayUntil() 函数中自动更新;它通常不会被应用程序代码修改,但必须在第一次使用之前将其初始化为当前滴答计数。 |
TickType_t xTimeIncrement | 此参数确定任务时间周期。该周期包含了任务执行时间及阻塞时间。xTimeIncrement 的单位是“ticks”。宏 pdMS_TO_TICKS() 可用于将指定的毫秒时间转换为刻度指定的时间。 |
实例
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
TickType_t xLastWakeTime;
pcTaskName = ( char * ) pvParameters;
/* xLastWakeTime变量必须被初始化为当前的tick count值。在之后,这个变量会被vTaskDelayUntil()自动更新为下一次离开阻塞的时间值*/
xLastWakeTime = xTaskGetTickCount();
for( ;; )
{
vPrintString( pcTaskName ); //这个任务的实际工作是打印出这个变量内容
/* 此任务准确的每 250 毫秒执行一次。 xLastWakeTime 在 vTaskDelayUntil() 中自动更新,因此任务不会显式更新。 */
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 250 ) );
} }
4 优先级操作函数
4.1 vTaskPrioritySet() 函数
说明
该函数用于在调度器scheduler启动后,必变任何任务的优先级。
注意:在FreeRTOSConfig.h中 INCLUDE_vTaskPrioritySet 宏设置为1时,vTaskPrioritySet()函数才有效。
声明
void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority)
参数
参数 | 说明 |
---|---|
TaskHandle_t pxTask | 需要被修改优先级的任务的句柄。在任务创建时分配的。 |
UBaseType_t uxNewPriority | 需要设定的优先级。 |
4.2 uxTaskPriorityGet() 函数
说明
该函数用于查询指定任务的优先级。在FreeRTOSConfig.h中 INCLUDE_uxTaskPriorityGet宏设置为1时,uxTaskPriorityGet()才有效
声明
UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );
参数
参数 | 说明 |
---|---|
TaskHandle_t pxTask | 要查询的任务句柄。 |
返回值
返回查询任务的优先级。
实例
说明:
- task1是以最高优先级创建的,因此保证首先运行。在将task2的优先级提高到高于其自身优先级之前,任务1打印出几个字符串。
- task2一具有最高的相对优先级就开始运行(进入运行状态)。任何时候只能有一个任务处于运行状态,因此当task2处于运行状态时,task1处于就绪状态。
- task2在将自己的优先级设置回低于task1之前打印出一条消息。
- task2将其优先级降低意味着task1再次成为最高优先级的任务,因此task1重新进入运行状态,迫使task2返回就绪状态。
void vTask1( void *pvParameters )
{
UBaseType_t uxPriority;
uxPriority = uxTaskPriorityGet( NULL ); //参数为NULL意为返回调用本函数的任务的优先级
for( ;; )
{
vPrintString( "Task 1 is running\r\n" );
vPrintString( "About to raise the Task 2 priority\r\n" );
vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
/* 到这一步,调度器会切换task2任务运行。 */
} }
void vTask2( void *pvParameters )
{
UBaseType_t uxPriority;
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* For this task to reach this point Task 1 must have already run and
set the priority of this task higher than its own.
Print out the name of this task. */
vPrintString( "Task 2 is running\r\n" );
vPrintString( "About to lower the Task 2 priority\r\n" );
vTaskPrioritySet( NULL, ( uxPriority - 2 ) ); //把task2的任务优先级又改回低于task1,切换到task1运行。
} }
以下是main函数的定义:
TaskHandle_t xTask2Handle = NULL;
int main( void )
{
xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
vTaskStartScheduler();
for( ;; );
}
5 任务删除函数
说明
本函数用于删除任务自已或其它任务。删除的任务不存在了,所以不能再进入Running状态。
不要试图使用任务句柄去引用一个已被删除的任务。
当一个任务被删除时,空闲任务负责释放用于保存被删除任务的堆栈和数据结构(任务控制块)的内存。因此,如果应用程序使用 vTaskDelete() API 函数,那么应用程序还必须确保空闲任务不会被处理时间不足(空闲任务必须在运行状态下分配时间)。
只有由内核自己分配的内存会在一个任务被删除时被自动释放的。内存,或任何其它资源,这些由应用程序(而不是内核)分配给任务的,必须被应用程序在任务删除时显式的释放。
声明
void vTaskDelete( TaskHandle_t pxTaskToDelete );
参数
参数 | 说明 |
---|---|
TaskHandle_t pxTaskToDelete | 要被删除的任务句柄 |
如果参数等于NULL则是删除调用该函数的任务自已。
6 总结
任务是FreeRTOS最重要的概念。因此回绕任务的概念比较多。从使用的角度出发,需要明白任务是一段带无限循环的永不退出的函数。任务句柄是系统操控任务的重要指针。任务之间的调度重要依据是优先级。优先级数值越高就越优先被调度。任务会有四种不同的工作状态。开发者对任务的使用主要是任务的创建,任务的优先级修改,任务的删除。