《FreeRTOS任务删除篇》
任务删除函数
- 源码
- 1. 进入临界区
- 1.1 第一步
- 1.2 第二步
- 1.3 第三步
- 1.4 第四步
- 2. 获取待删除任务的任务控制块TCB
- 3. 从就绪/延迟列表中删除任务
- 4. 从事件列表中删除任务
- 5. 如果待删除任务是当前运行的任务
- 6. 如果待删除任务是其它任务
- 7. 退出临界区
- 7.1 第一步
- 7.2 第二步
- 7.3 第三步
- 7.4 第四步
- 8. 如果待删除的任务不是当前运行的任务
- 9. 如果待删除的任务不是当前运行的任务
- 介绍FreeRTOS任务删除函数的执行过程,逐行分析源代码。
- 要使用任务删除函数vTaskDelete,需要配置宏INCLUDE_vTaskDelete为1。
- 删除任务函数vTaskDelete的参数为待删除任务的任务句柄(即任务控制块)。
- 当函数参数为NULL时,代表删除的是调用函数的任务本身(即正在运行的任务)。
源码
/* 示例,在正在运行的任务中调用任务删除函数。*/
vTaskDelete(NULL);
/* 任务删除 */
#if ( INCLUDE_vTaskDelete == 1 ) /* 需要INCLUDE_vTaskDelete宏配置为1 */
void vTaskDelete( TaskHandle_t xTaskToDelete ) /* 函数参数为待删除任务的任务句柄,可以用NULL代替当前任务句柄。 */
{
TCB_t * pxTCB;
taskENTER_CRITICAL(); /* 进入临界区,本质是关闭全局中断。 */
{
/* If null is passed in here then it is the calling task that is
* being deleted.
* 如果在此处传递NULL,则删除的是调用函数的任务本身。
*/
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/* Remove task from the ready/delayed list.
* 从就绪/延迟列表中删除任务
* 如果删除后列表中没有列表项,则uxListRemove返回0
*/
/* 将任务控制块的成员状态列表项xStateListItem从列表中移除 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Is the task waiting on an event also?
* 任务是否也在等待事件?
* 任务控制块的成员xEventListItem是否挂载在其他列表中
*/
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
/* 将事件列表项从等待事件列表中移出 */
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Increment the uxTaskNumber also so kernel aware debuggers can
* detect that the task lists need re-generating. This is done before
* portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
* not return.
* 同时自增uxTaskNumber,以便内核感知调试器可以检测到任务列表需要重新生成。
* 这是在portPRE_TASK_DELETE_HOOK之前完成的,因为在Windows端口中,宏不会返回
*/
uxTaskNumber++; /* 任务列表需要重新生成 */
/* 删除的是调用任务本身时 */
if( pxTCB == pxCurrentTCB )
{
/* A task is deleting itself. This cannot complete within the
* task itself, as a context switch to another task is required.
* Place the task in the termination list. The idle task will
* check the termination list and free up any memory allocated by
* the scheduler for the TCB and stack of the deleted task.
* 一个任务正在删除自己。
* 这无法在任务本身内完成,因为需要切换到另一个任务的上下文。
* 将任务放入等待结束列表中。
* 空闲任务将检查等待结束列表,并释放调度器为准备删除任务分配的TCB和堆栈内存。
*/
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* Increment the ucTasksDeleted variable so the idle task knows
* there is a task that has been deleted and that it should therefore
* check the xTasksWaitingTermination list.
* 递增ucTasksDeleted变量,以便空闲任务知道有一个任务已被删除,
* 因此它应该检查xTasksWaitingTermination列表。
*/
++uxDeletedTasksWaitingCleanUp;
/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
* portPRE_TASK_DELETE_HOOK() does not return in the Win32 port.
* 在调用portPRE_TASK_delete_hook之前调用delete钩子,
* 因为portPRE_TAS K_delete_ hook在Win32端口中不返回。
*/
traceTASK_DELETE( pxTCB );
/* The pre-delete hook is primarily for the Windows simulator,
* in which Windows specific clean up operations are performed,
* after which it is not possible to yield away from this task -
* hence xYieldPending is used to latch that a context switch is
* required.
* 预删除挂钩主要用于Windows模拟器,
* 在该模拟器中执行特定于Windows的清理操作,之后不可能放弃此任务,
* 因此xYieldPending用于锁定需要进行上下文切换。
*/
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}
else
{
--uxCurrentNumberOfTasks; /* 当前任务个数自减一 */
traceTASK_DELETE( pxTCB );
/* Reset the next expected unblock time in case it referred to
* the task that has just been deleted.
* 重置下一个预期的解锁时间,以防它引用了刚刚被删除的任务
*/
prvResetNextTaskUnblockTime();
}
}
taskEXIT_CRITICAL(); /* 退出临界区,本质是开启全局中断 */
/* If the task is not deleting itself, call prvDeleteTCB from outside of
* critical section. If a task deletes itself, prvDeleteTCB is called
* from prvCheckTasksWaitingTermination which is called from Idle task.
* 如果任务不是删除自己,请从临界区外部调用prvDeleteTCB。
* 如果任务删除自己,则从空闲任务调用的prvCheckTasksWaitingTermination中调用prvDeleteTCB删除任务
*/
if( pxTCB != pxCurrentTCB )
{
prvDeleteTCB( pxTCB );
}
/* Force a reschedule if it is the currently running task that has just
* been deleted.
* 如果删除的是当前正在运行的任务,则强制重新开启一次任务调度
*/
if( xSchedulerRunning != pdFALSE ) /* 任务调度器正在运行中 */
{
if( pxTCB == pxCurrentTCB ) /* 删除的是当前正在运行的任务 */
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#endif /* INCLUDE_vTaskDelete */
1. 进入临界区
taskENTER_CRITICAL(); /* 进入临界区,本质是关闭全局中断。 */
1.1 第一步
#define taskENTER_CRITICAL()
portENTER_CRITICAL() /* 从任务中进入临界区 */
1.2 第二步
#define portENTER_CRITICAL()
vPortEnterCritical()
1.3 第三步
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
ulCriticalNesting++;
}
1.4 第四步
#define portDISABLE_INTERRUPTS()
__asm(" setc INTM") /* 关闭全局中断 */
2. 获取待删除任务的任务控制块TCB
portDONT_DISCARD PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; /* 指向当前运行的任务控制块。 */
/*
* Several functions take a TaskHandle_t parameter that can optionally be NULL,
* where NULL is used to indicate that the handle of the currently executing
* task should be used in place of the parameter. This macro simply checks to
* see if the parameter is NULL and returns a pointer to the appropriate TCB.
* 有几个函数接受一个TaskHandle_t参数,该参数可以选择设置为NULL,
* 其中NULL用于表示使用当前正在执行的任务的句柄来代替该参数。
* 此宏只是检查参数是否为NULL,并返回指向相应TCB的指针。
*/
#define prvGetTCBFromHandle( pxHandle )
( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) )
3. 从就绪/延迟列表中删除任务
/* Remove task from the ready/delayed list.
* 从就绪/延迟列表中删除任务
* 如果删除后列表中没有列表项,则返回0
*/
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 从列表中移除待移除的列表项 */
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
* item.
* 从列表项的成员pxContainer可以知道它在哪个列表中。从列表项中获取列表。
* */
List_t * const pxList = pxItemToRemove->pxContainer;
/* 将列表项从列表中移除 */
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing.
* 仅在决策覆盖率测试期间使用。
*/
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item.
* 确保索引指向有效项
*/
if( pxList->pxIndex == pxItemToRemove ) /* 如果列表的列表索引号指向待移除的列表项 */
{
pxList->pxIndex = pxItemToRemove->pxPrevious; /* 将索引号指向待移除列表项的前一个列表项 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pxContainer = NULL; /* 将待删除列表项的成员pxContainer设置为NULL,表示其不属于任何列表 */
( pxList->uxNumberOfItems )--; /* 列表的列表项个数减一 */
return pxList->uxNumberOfItems; /* 返回列表中剩余的列表项个数 */
}
4. 从事件列表中删除任务
/* Is the task waiting on an event also?
* 任务是否也在等待事件?
* 任务控制块的成员xEventListItem是否挂载在其他列表中
*/
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
/* 将事件列表项从等待事件列表中移出 */
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*
* Return the list a list item is contained within (referenced from).
* 返回列表项所挂载的列表
*
* @param pxListItem The list item being queried. pxListItem表示正在查询的列表项。
* @return A pointer to the List_t object that references the pxListItem. 指向挂载pxListItem的List_t对象的指针
*/
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
5. 如果待删除任务是当前运行的任务
- 将列表项xStateListItem按尾部插入法挂载到xTasksWaitingTermination列表中
- 变量uxDeletedTasksWaitingCleanUp自增一,以供空闲任务使用。
/* 删除的是调用任务本身时 */
if( pxTCB == pxCurrentTCB )
{
/* A task is deleting itself. This cannot complete within the
* task itself, as a context switch to another task is required.
* Place the task in the termination list. The idle task will
* check the termination list and free up any memory allocated by
* the scheduler for the TCB and stack of the deleted task.
* 一个任务正在删除自己。
* 这无法在当前任务执行内部完成,因为还需要切换到另一个任务的上下文。
* 因此将待删除任务放入等待结束任务列表中。
* 空闲任务将检查等待结束任务列表,并释放调度器为待删除任务分配的TCB和堆栈内存。
*/
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* Increment the ucTasksDeleted variable so the idle task knows
* there is a task that has been deleted and that it should therefore
* check the xTasksWaitingTermination list.
* 递增ucTasksDeleted变量,以便空闲任务知道有一个任务已被删除,
* 因此它应该检查xTasksWaitingTermination列表。
* */
++uxDeletedTasksWaitingCleanUp;
/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
* portPRE_TASK_DELETE_HOOK() does not return in the Win32 port.
* 在调用portPRE_TASK_delete_hook之前调用delete钩子,
* 因为portPRE_TAS K_delete_ hook在Win32端口中不返回。
* */
traceTASK_DELETE( pxTCB );
/* The pre-delete hook is primarily for the Windows simulator,
* in which Windows specific clean up operations are performed,
* after which it is not possible to yield away from this task -
* hence xYieldPending is used to latch that a context switch is
* required.
* 预删除挂钩主要用于Windows模拟器,
* 在该模拟器中执行特定于Windows的清理操作,之后不可能放弃此任务,
* 因此xYieldPending用于锁定需要进行上下文切换。
* */
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}
6. 如果待删除任务是其它任务
--uxCurrentNumberOfTasks; /* 当前任务个数自减一 */
traceTASK_DELETE( pxTCB );
/* Reset the next expected unblock time in case it referred to
* the task that has just been deleted.
* 重置下一个预期的解锁时间,以防它引用了刚刚被删除的任务,即下一个解除等待准备运行的任务就是待删除任务。
*/
prvResetNextTaskUnblockTime();
/* 重置下一个任务解锁时间 */
static void prvResetNextTaskUnblockTime( void )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) /* 延迟任务列表是否为空 */
{
/* The new current delayed list is empty. Set xNextTaskUnblockTime to
* the maximum possible value so it is extremely unlikely that the
* if( xTickCount >= xNextTaskUnblockTime ) test will pass until
* there is an item in the delayed list.
* 当前新的延迟列表为空。将xNextTaskUnblockTime设置为最大可能值
*/
xNextTaskUnblockTime = portMAX_DELAY;
}
else
{
/* The new current delayed list is not empty, get the value of
* the item at the head of the delayed list. This is the time at
* which the task at the head of the delayed list should be removed
* from the Blocked state.
* 当前新的延迟列表不为空,请获取延迟列表头部项目的值。
* 此时,应将延迟列表开头的任务从“阻塞”状态中删除。
* 即准备让下一个被阻塞的任务开始执行
*/
xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList );
}
}
/*
* Access macro to determine if a list contains any items. The macro will
* only have the value true if the list is empty.
* 访问宏以确定列表是否包含任何列表项。只有当列表为空时,宏才会具有值true。
*/
#define listLIST_IS_EMPTY( pxList )
( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. 指向当前正在使用的延迟任务列表*/
#define portMAX_DELAY
( TickType_t ) 0xffffffffUL
/*
* Access macro to retrieve the value of the list item at the head of a given list.
* 访问宏以检索给定列表开头的列表项的值
*/
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )
( ( ( pxList )->xListEnd ).pxNext->xItemValue ) /* 末尾列表项的下一个列表项即为头部列表项。 */
PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. 在调度程序启动之前,已初始化为portMAX_DELAY*/
7. 退出临界区
taskEXIT_CRITICAL(); /* 退出临界区,本质是开启全局中断 */
7.1 第一步
#define taskEXIT_CRITICAL()
portEXIT_CRITICAL()
7.2 第二步
#define portEXIT_CRITICAL()
vPortExitCritical()
7.3 第三步
void vPortExitCritical( void )
{
ulCriticalNesting--;
if( ulCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
7.4 第四步
#define portENABLE_INTERRUPTS()
__asm(" clrc INTM") /* 打开全局中断 */
8. 如果待删除的任务不是当前运行的任务
/* If the task is not deleting itself, call prvDeleteTCB from outside of
* critical section. If a task deletes itself, prvDeleteTCB is called
* from prvCheckTasksWaitingTermination which is called from Idle task.
* 如果任务不是删除自己,请从临界区外部调用prvDeleteTCB。
* 如果任务删除自己,则从空闲任务调用的prvCheckTasksWaitingTermination中调用prvDeleteTCB删除任务
*/
if( pxTCB != pxCurrentTCB )
{
prvDeleteTCB( pxTCB );
}
/* 删除任务控制块,根据分配内存的方式释放TCB和堆栈内存 */
#if ( INCLUDE_vTaskDelete == 1 )
static void prvDeleteTCB( TCB_t * pxTCB )
{
/* This call is required specifically for the TriCore port. It must be
* above the vPortFree() calls. The call is also used by ports/demos that
* want to allocate and clean RAM statically.
* 此调用是TriCore端口特有的。它必须位于vPortFree调用之上。
* 该调用也被希望静态分配和清理RAM的端口/演示使用。
* */
portCLEAN_UP_TCB( pxTCB );
#if ( ( configUSE_NEWLIB_REENTRANT == 1 ) || ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) )
{
/* Free up the memory allocated for the task's TLS Block. */
configDEINIT_TLS_BLOCK( pxCurrentTCB->xTLSBlock );
}
#endif
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
{
/* The task can only have been allocated dynamically - free both
* the stack and TCB.
* 该任务只能是被动态分配,需要释放堆栈和TCB
* */
vPortFreeStack( pxTCB->pxStack );
vPortFree( pxTCB );
}
#elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
{
/* The task could have been allocated statically or dynamically, so
* check what was statically allocated before trying to free the
* memory.
* 任务可以静态或动态分配,因此在尝试释放内存之前,请检查静态分配的内容
* */
if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
{
/* Both the stack and TCB were allocated dynamically, so both
* must be freed.
* 堆栈和TCB都是动态分配的,因此必须释放它们。
* */
vPortFreeStack( pxTCB->pxStack );
vPortFree( pxTCB );
}
else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
{
/* Only the stack was statically allocated, so the TCB is the
* only memory that must be freed.
* 只有堆栈是静态分配的,因此TCB是唯一必须释放的内存。
* */
vPortFree( pxTCB );
}
else
{
/* Neither the stack nor the TCB were allocated dynamically, so
* nothing needs to be freed.
* 堆栈和TCB都不是动态分配的,因此不需要释放任何内容
* */
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB );
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
#endif /* INCLUDE_vTaskDelete */
9. 如果待删除的任务不是当前运行的任务
/* Force a reschedule if it is the currently running task that has just
* been deleted.
* 如果删除的是当前正在运行的任务,则强制重新开启一次任务调度
*/
if( xSchedulerRunning != pdFALSE ) /* 任务调度器正在运行中 */
{
if( pxTCB == pxCurrentTCB ) /* 删除的是当前正在运行的任务 */
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE;
#ifndef portYIELD_WITHIN_API
#define portYIELD_WITHIN_API portYIELD
#endif
#define portYIELD()
do
{
bYield = 1;
__asm(" INTR INT14");
}while(0)