1. 任务删除API函数
FreeRTOS中任务删除的相关API函数如下:
函数 | 描述 |
---|---|
vTaskDelete() | 删除一个任务 |
其它内部调用函数:
函数 | 描述 |
---|---|
prvGetTCBFromHandle() | 该函数是一个宏定义,获取任务控制块,若参数为NULL,则获取当前正在运行任务的任务控制块 |
taskRESET_READY_PRIORITY() | 清除相应的就绪标志位 |
prvDeleteTCB() | 根据堆栈和任务控制块创建的方式,回收相应的堆栈和任务控制块空间 |
prvResetNextTaskUnblockTime() | 重置下一个任务的解锁时间 |
2. 任务的删除函数vTaskDelete()
任务删除函数使用vTaskDelete(),可以删除一个由xTaskCreate() 或 xTaskCreateStatic()创建的任务。使用此函数,必须将INCLUDE_vTaskDelete 设置为1,在FreeRTOSConfig.h定义中,其函数原型如下:
/********************************************************
参数:xTaskToDelete :需要删除的任务句柄,若为NULL,则删除任务本身
返回:无
*********************************************************/
void vTaskDelete( TaskHandle_t xTaskToDelete )
源代码如下:
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
TCB_t *pxTCB;
/* 进入临界状态 */
taskENTER_CRITICAL();
{
/* 获取任务的控制块,若参数为NULL,则获取正在执行的任务控制块,即自己删除自己 */
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/*
* 删除任务的状态信息,不让任务的状态列表项挂接到任何一个列表上
* 不同优先级的任务放在不同任务就绪列表,用一个32位变量表示就绪位,相应Bit位为1表示对应就绪表中有任务
* 如返回值为0,则说明该任务优先级下,任务就绪表只有它一个任务,删除了该任务,就无其他任务
*/
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 清除相应的就绪标志位,防止调度器调用 */
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 判断任务是否存在事件(如信号量,队列),若存在,则将事件从相应的列表中删除 */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 通知 (kernel aware debuggers) 更新任务列表 */
uxTaskNumber++;
/* 如果获取的任务控制块是当前正在执行的任务 */
if( pxTCB == pxCurrentTCB )
{
/* 将任务的状态列表项插入到xTasksWaitingTermination列表中(勉强先称为等待删除列表) */
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* 记录等待删除的任务数,空闲任务时释放空间 */
++uxDeletedTasksWaitingCleanUp;
/* 钩子函数,需要用户自己实现 */
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}
else
{
/* 如果删除其它任务,不是本身,则直接删除,当前总任务数减1 */
--uxCurrentNumberOfTasks;
/* 根据创建任务时的pxTCB->ucStaticallyAllocated 做的标记,释放相应的空间 */
prvDeleteTCB( pxTCB );
/* 重置下一个任务的解锁时间,防止下一个解锁时间的任务为刚刚删除的任务 */
prvResetNextTaskUnblockTime();
}
traceTASK_DELETE( pxTCB );
}
taskEXIT_CRITICAL();
/* 如果任务调度器已经开启 */
if( xSchedulerRunning != pdFALSE )
{
/* 如果获取的删除任务是现在正在执行的任务 */
if( pxTCB == pxCurrentTCB )
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API(); /* 执行一次任务切换,因为正在执行的任务已经从就绪表中删除了 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
说明:
- 该函数把删除的任务从各个列表中删除,若删除的是当前正在执行的任务,先放入等待删除列表中,等到空闲任务才真正删除该任务。
- 在任务创建时,堆栈和任务控制块的有不同的创建方式,则在任务删除时会用不同的方式回收空间。
- 若通过动态方式创建的任务,会自动回收空间,静态方式需要通过程序员自身释放空间。
3. 清除相应的就绪标志位函数taskRESET_READY_PRIORITY()
该函数使用宏定义实现,该函数清除相应的就绪标志位,在task.c中定义,源代码如下:
#define taskRESET_READY_PRIORITY( uxPriority ) \
{ \
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \
{ \
portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \
} \
}
其中:
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
说明:
- 每个优先级都会有一个就绪列表,不同优先级任务放在不同的就绪列表中,同一优先级放在同一个就绪列表中。
- 该函数先判断对应优先级的就绪列表中是否有任务,如果无任务,就把相应的就绪标志位置0。
- taskRECORD_READY_PRIORITY( uxPriority )表示将相应的就绪标志位置1,与该函数相反功能。
4. 删除任务控制块函数prvDeleteTCB()
使用此函数,必须将INCLUDE_vTaskDelete 设置为1,在FreeRTOSConfig.h定义中,其函数原型如下:
/********************************************************
参数:xTaskToDelete :需要删除的任务句柄
返回:无
*********************************************************/
void vTaskDelete( TaskHandle_t xTaskToDelete )
函数源代码如下:
static void prvDeleteTCB( TCB_t *pxTCB )
{
portCLEAN_UP_TCB( pxTCB );
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_reclaim_reent( &( pxTCB->xNewLib_reent ) );
}
#endif /* configUSE_NEWLIB_REENTRANT */
/* 如果仅使用动态方式创建任务,则自动释放堆栈和任务控制块内存 */
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
{
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}
#elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) /* 同时使能了动态和静态创建任务 */
{
/* 如果任务创建时,标记使用动态分配的堆栈和任务控制块,则自动释放 */
if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
{
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}
/* 如果任务创建时,标记使用仅静态分配堆栈,则自动释放任务控制块 */
else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
{
vPortFree( pxTCB );
}
else
{
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB )
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
5. 函数prvResetNextTaskUnblockTime()
该函数重置下一个任务的解锁时间,防止下一个任务的解锁时间就是刚刚删除的任务。
延时列表会根据任务的延时时间先后插入到延时列表中,延时时间短的在前,延时时间长的在后。同时,下一个要唤醒的任务时间值保存在xNextTaskUnblockTime中。
函数源代码如下:
static void prvResetNextTaskUnblockTime( void )
{
TCB_t *pxTCB;
/* 如果延时列表中uxNumberOfItems为0,条件满足,即延时列表为空 */
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
xNextTaskUnblockTime = portMAX_DELAY;
}
else /* 延时列表不为空 */
{
/* 获取延时列表中第一个列表项的任务控制块 */
( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
/* 延时列表中第一个列表项的延时时间赋值给xNextTaskUnblockTime */
xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
}
}