FreeRTOS之vTaskDelete实现分析

这里写自定义目录标题

  • [1 函数接口](#1 函数接口)
    • [1.1 函数接口](#1.1 函数接口)
    • [1.2 函数参数简介](#1.2 函数参数简介)
  • [2 vTaskDelete的调用关系](#2 vTaskDelete的调用关系)
    • [2.1 调用关系](#2.1 调用关系)
    • [2.2 调用关系示意图](#2.2 调用关系示意图)
  • [3 函数源码分析](#3 函数源码分析)
    • [3.1 vTaskDelete](#3.1 vTaskDelete)
    • [3.2 uxListRemove](#3.2 uxListRemove)

1 函数接口

1.1 函数接口

c 复制代码
void vTaskDelete( TaskHandle_t xTaskToDelete )

1.2 函数参数简介

  • TaskHandle_t xTaskToDelete 要删除任务的句柄,但这个句柄其实就是我们申请任务时创建的TCB,这个参数在传入的时候可以设置为NULL,表示要删除的是自己当前的任务。

2 vTaskDelete的调用关系

2.1 调用关系

c 复制代码
|- vTaskDelete
	|- pxTCB = prvGetTCBFromHandle( xTaskToDelete );
	|- uxListRemove( &( pxTCB->xStateListItem ) )
	|- taskRESET_READY_PRIORITY( pxTCB->uxPriority );
	|- vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
	|- taskYIELD_WITHIN_API();

2.2 调用关系示意图

3 函数源码分析

3.1 vTaskDelete

  • pxTCB = prvGetTCBFromHandle( xTaskToDelete ); 获取要删除任务的TCB
  • uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 将当前要删除的任务从pxReadyTasksLists对应优先级的链表里面删除,并判断该优先级的任务数是不是为0了,如果当前该优先级的链表里已经没有任务则需要调用taskRESET_READY_PRIORITY( pxTCB->uxPriority );去做重置将该优先级在uxTopReadyPriority中mask掉。
  • vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); 如果当前的任务处于运行状态,则将当前要删除的任务放入到xTasksWaitingTermination列表,等到idle task执行的时候再去执行删除操作。任务是否处于调度状态的判断依据是xSchedulerRunning是否是pdTRUE,该状态位在执行vTaskStartScheduler函数时被置位,在执行vTaskEndScheduler函数时被置位pdFALSE,因此只要是当前的系统调度没有停就需要将要删除的任务的TCB加入到xTasksWaitingTermination链表中。
  • ++uxDeletedTasksWaitingCleanUp 记录等待清理任务的数量,在idle task或者vTaskEndScheduler执行时,以该全局变量作为清理数量的标准。
  • xDeleteTCBInIdleTask = pdTRUE; 标记当前任务要再idle task里面做删除工作。
  • taskYIELD_WITHIN_API();或者prvYieldCore( pxTCB->xTaskRunState ); 将处理器让出去,调度其他的任务。
c 复制代码
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
	TCB_t * pxTCB;
	BaseType_t xDeleteTCBInIdleTask = pdFALSE;
	BaseType_t xTaskIsRunningOrYielding;

	traceENTER_vTaskDelete( xTaskToDelete );

	taskENTER_CRITICAL();
	{
		获取当前要删除任务的TCB,如果xTaskToDelete 为空则表示要删除当前正在运行的任务,否则就找到对应任务的TCB。
		pxTCB = prvGetTCBFromHandle( xTaskToDelete );
		configASSERT( pxTCB != NULL );

		把当前要删除的任务从ready/delayed 链表中删除,并判断任务优先级的链表任务数是否为0
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
		{
			如果当前要删除的任务优先级所对应的链表任务数为0,则将该优先级在uxTopReadyPriority中mask掉。
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		判断当前的任务是否在一个event的链表,如果是则通过uxListRemove将其移除
		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++;

		通过taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD来判断当前的任务是否在运行状态,如果其xTaskRunState被设置为-1则表明其不处于运行状态,正常运行的任务其xTaskRunState值会被设置为所运行的core的id。
		xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB );

		根据xSchedulerRunning 来判断当前的是否还处于正常的调度状态,该全局变量在vTaskStartScheduler函数被执行时设置为pdTRUE,在vTaskEndScheduler时被设置为pdFALSE
		if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) )
		{
			为了防止该任务在其他core上还有运行的,所以将该任务添加到xTasksWaitingTermination链表上,等idle task运行时再做清理工作。
			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

			标记当前需要做清理的任务数
			++uxDeletedTasksWaitingCleanUp;

			/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
			 * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
			traceTASK_DELETE( pxTCB );

			用以标记有任务需要删除,如果需要设置为pdTRUE,则在后面会调用prvDeleteTCB( pxTCB );释放掉这块内存空间。
			xDeleteTCBInIdleTask = pdTRUE;

			/* 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. */
			#if ( configNUMBER_OF_CORES == 1 )
				portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) );
			#else
				portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ pxTCB->xTaskRunState ] ) );
			#endif

			在SMP系统给按任务让出CPU,让其他任务去运行
			#if ( configNUMBER_OF_CORES > 1 )
			{
				if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
				{
					if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
					{
						configASSERT( uxSchedulerSuspended == 0 );
						taskYIELD_WITHIN_API();
					}
					else
					{
						prvYieldCore( pxTCB->xTaskRunState );
					}
				}
			}
			#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
		}
		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. */
	if( xDeleteTCBInIdleTask != pdTRUE )
	{
		prvDeleteTCB( pxTCB );
	}

	/* Force a reschedule if it is the currently running task that has just
	 * been deleted. */
	#if ( configNUMBER_OF_CORES == 1 )
	{
		if( xSchedulerRunning != pdFALSE )
		{
			if( pxTCB == pxCurrentTCB )
			{
				configASSERT( uxSchedulerSuspended == 0 );
				taskYIELD_WITHIN_API();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
	#endif /* #if ( configNUMBER_OF_CORES == 1 ) */

	traceRETURN_vTaskDelete();
}

3.2 uxListRemove

  • pxList = pxItemToRemove->pxContainer; 通过pxContainer找到当前任务优先级所对应链表的链表头
  • pxList->uxNumberOfItems 返回当前任务优先级链表还剩余的任务数,一般用于判断链表所挂任务是否为0,用以来确定当前任务的优先级是否需要mask掉。
c 复制代码
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
    /* The list item knows which list it is in.  Obtain the list from the list
     * item. */
    List_t * const pxList = pxItemToRemove->pxContainer;

    traceENTER_uxListRemove( pxItemToRemove );

    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;
    ( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems - 1U );

    traceRETURN_uxListRemove( pxList->uxNumberOfItems );

    return pxList->uxNumberOfItems;
}