《FreeRTOS任务删除篇》

时间:2024-11-21 07:03:08

任务删除函数

  • 源码
  • 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. 如果待删除的任务不是当前运行的任务

  1. 介绍FreeRTOS任务删除函数的执行过程,逐行分析源代码。
  2. 要使用任务删除函数vTaskDelete,需要配置宏INCLUDE_vTaskDelete为1。
  3. 删除任务函数vTaskDelete的参数为待删除任务的任务句柄(即任务控制块)。
  4. 当函数参数为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. 如果待删除任务是当前运行的任务

  1. 将列表项xStateListItem按尾部插入法挂载到xTasksWaitingTermination列表中
  2. 变量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)