FreeRTOS原理剖析:任务删除

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 ) );
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值