FreeRTOS-任务删除、挂起、恢复源码分析

由于后面的源码分析会涉及到一些任务的操作函数,所以这里先分析一下任务的一些相关操作,包括任务的删除、挂起、恢复操作。

任务删除:

#if ( INCLUDE_vTaskDelete == 1 )

	void vTaskDelete( TaskHandle_t xTaskToDelete )
	{
	TCB_t *pxTCB;

		/* 进入临界区 */
		taskENTER_CRITICAL();
		{
			/* 如果没有指定删除的任务(参数为NULL),则默认删除当前任务(即自己删自己),否则删除形参指定的任务 */
			/* #define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) )
 */
			pxTCB = prvGetTCBFromHandle( xTaskToDelete );

			/* 将任务从就绪列表中删除 */
			if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
			{
				/* 清除对应的就绪标志 */
				taskRESET_READY_PRIORITY( pxTCB->uxPriority );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* 如果任务有等待事件(如信号量、队列等),将任务从相应的事件列表中删除 */
			/* #define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) */
			if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
			{
				( void ) uxListRemove( &( pxTCB->xEventListItem ) );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* 递增任务数量,空闲任务根据这个变量知道有任务需要删除 */
			uxTaskNumber++;

			if( pxTCB == pxCurrentTCB )
			{
				/* 如果要删除的任务是当前任务(即自己删自己) */
				
				/* 将任务插入到等待删除列表,不能立即删除的原因是由于任务控制块和堆栈的内存不能被立即释放,
				必须等到当前任务运行完才能释放相应的内存,这里做个标记到
				xTasksWaitingTermination列表中,在空闲任务内会将任务相关内存进行释放 */
				vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

				/* 等待删除任务数递增 */
				++uxDeletedTasksWaitingCleanUp;

				/* 调用任务删除钩子函数,需要自行实现 */
				portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
			}
			else
			{
				/* 如果删除的任务不是自己,是其它任务则当前任务数量递减 */
				--uxCurrentNumberOfTasks;
				/* 删除的是其它任务,这里直接删除对应的任务控制块,函数里面使用内存管理删除任务TCB和堆栈,内存管理后面分析 */
				prvDeleteTCB( pxTCB );

				/* 重置下一个任务的解锁定时间,防止有任务的解锁时间参考了刚刚删除的任务,这个函数的实现比较简单就不放源码了 */
				prvResetNextTaskUnblockTime();
			}

			traceTASK_DELETE( pxTCB );
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		if( xSchedulerRunning != pdFALSE )
		{
			if( pxTCB == pxCurrentTCB )
			{
				configASSERT( uxSchedulerSuspended == 0 );
				/* 如果调度器在运行,并且是自己删除自己(此任务当前正在运行),则强制一次任务切换 */
				portYIELD_WITHIN_API();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}

#endif

任务挂起
任务进入挂起态后不能被调度器调用进入运行态,而且任务没有超过时间,进入挂起态和退出挂起(恢复)态使用函数 vTaskSuspend() 和 vTaskResume()。

void vTaskSuspend( TaskHandle_t xTaskToSuspend )
	{
	TCB_t *pxTCB;

		/* 进入临界区 */
		taskENTER_CRITICAL();
		{
			/* 如果没有指定挂起的任务(参数为NULL),则默认挂起当前任务(即自己挂起自己),否则挂起指定的任务 */
			/* #define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) */
			pxTCB = prvGetTCBFromHandle( xTaskToSuspend );

			traceTASK_SUSPEND( pxTCB );
			
			/* 从就绪或延时列表中删除任务 */
			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();
			}

			/* 将任务插入到挂起列表中 */
			vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		if( xSchedulerRunning != pdFALSE )
		{
			taskENTER_CRITICAL();
			{
				/* 如果调度器在运行,进入临界区重置下一个任务的解锁定时间,防止有任务的解锁时间参考了刚刚删除的任务。 */
				prvResetNextTaskUnblockTime();
			}
			taskEXIT_CRITICAL();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		if( pxTCB == pxCurrentTCB )
		{
			if( xSchedulerRunning != pdFALSE )
			{
				/* 如果调度器在运行,并且是自己挂起自己(此任务当前正在运行),则强制一次任务切换 */
				configASSERT( uxSchedulerSuspended == 0 );
				portYIELD_WITHIN_API();
			}
			else
			{
				/* 调度器已经停止运行,但pxCurrentTCB指向的任务刚刚被挂起,需要手动去调整pxCurrentTCB的指向 */
				
				if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
				{
					/* 所有任务都被挂起了,变量置空(一般不会执行到这里,因为至少有一个空闲任务可以执行) */
					pxCurrentTCB = NULL;
				}
				else
				{
					/* 获取下一个要运行的任务(此函数任务切换章节已分析) */
					vTaskSwitchContext();
				}
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

任务恢复

void vTaskResume( TaskHandle_t xTaskToResume )
	{
	TCB_t * const pxTCB = xTaskToResume;
	
		configASSERT( xTaskToResume );


		/* 解挂的任务不是当前执行的任务(不存在恢复正在运行的任务这种情况),并且任务不为空 */
		if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
		{
			/* 进入临界区 */
			taskENTER_CRITICAL();
			{
				if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
				{
					/* 要恢复的任务当前处于挂起状态 */
					
					traceTASK_RESUME( pxTCB );

					/* 从挂起列表中删除任务,并将任务加入到就绪列表中 */
					( void ) uxListRemove(  &( pxTCB->xStateListItem ) );
					prvAddTaskToReadyList( pxTCB );

					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
					{
						/* 如果解挂的任务优先级比当前任务优先级高,则执行一次调度 */
						taskYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			/* 退出临界区 */
			taskEXIT_CRITICAL();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

上面判断任务是不是处于挂起状态使用了这个函数:

static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask )
	{
	BaseType_t xReturn = pdFALSE;
	const TCB_t * const pxTCB = xTask;

		if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE )
		{
			/* 任务处于挂起列表 */

			if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE )
			{
				/* 任务不处于挂起就绪列表(有任务获取到了相应的事件而解除了阻塞状态后本应该放到就绪列表中,但调度器还处于休眠状态,就会将任务暂时添加到xPendingReadyList这个列表) */
				if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) 
				{
					/* 任务没等待任何事件返回pdTRUE */
					xReturn = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		return xReturn;
	}

还有个任务恢复函数 xTaskResumeFromISR 用于中断中恢复任务,处理过程和 vTaskResume 基本一致。

你可能感兴趣的:(FreeRTOS)