FreeRTOS任务删除源码分析

前面的文章讲解了任务的创建函数,本章讲解任务的删除函数。在FreeRTOS中任务删除函数是一个条件编译函数。想要使用任务函数需要将宏INCLUDE_vTaskDelete定义为1。如果系统中的任务永远都不需要再运行了,那么就可以调用任务删除函数删除任务。
瞎哔哔谁都会,给我看代码。

#if ( INCLUDE_vTaskDelete == 1 )
	void vTaskDelete( TaskHandle_t xTaskToDelete )
	{
	TCB_t *pxTCB;

		taskENTER_CRITICAL();
		{
			/* If null is passed in here then it is the calling task that is
			being deleted. */
			pxTCB = prvGetTCBFromHandle( xTaskToDelete );//根据任务句柄获取任务控制块

			/* Remove task from the ready list. */
			//将任务从自己所在的列表中移除 此任务此时的状态不确定,所以不能确定此任务在哪个链表下挂载
			if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
			{
				taskRESET_READY_PRIORITY( pxTCB->uxPriority );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* Is the task waiting on an event also? */
			//如果任务有等待事件,则从事件列表中删除、
			//判断事件列表项是否挂载到列表, 如果挂载到了列表就将其从列表中删除
			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++;

			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. */
				//当前任务不能立即删除,因为还要继续运行
				//先添加到已删除但未释放内存的待处理列表中 在空闲任务中删除
				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. */
				++uxDeletedTasksWaitingCleanUp;//已经删除但未释放内存的任务个数++

				/* 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. */
				portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );//未实现的空宏
			}
			else
			{
				--uxCurrentNumberOfTasks;//任务数量减1
				prvDeleteTCB( pxTCB );//删除任务控制块

				/* Reset the next expected unblock time in case it referred to
				the task that has just been deleted. */
				prvResetNextTaskUnblockTime();//复位下次任务解锁时间
			}

			traceTASK_DELETE( pxTCB );
		}
		taskEXIT_CRITICAL();

		/* 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、prvGetTCBFromHandle是一个宏,传入任务句柄的地址,返回任务控制块地址。
通过prvGetTCBFromHandle获取任务控制块。其实任务句柄指针指向的地址就是任务控制块的地址。如果传入NULL,直接返回当前任务控制块的地址。很简单,就是一个问号语句。内部实现如下

#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) 

2、if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )将要删除任务的状态列表项从其所在的列表中删除,此时任务的状态列表项在哪一条列表并不确定。但是官方注释写的是从就绪列表中删除。个人认为此处的注释写的不准确。举个例子,如果把一个任务先挂起再删除,那么这个任务的状态列表项所在的列表就是挂起列表啊(这是我个人的见解,如果我的理解有误请私信或评论告诉我,十分感谢)。接下来就是复位优先级。复位优先级函数如下。

//复位的时候必须判断一下对应优先级下就绪列表是否为空,为空就复位优先级,不为空就不复位优先级
#define taskRESET_READY_PRIORITY( uxPriority )														\
{																									\
	if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 )	\
	{																								\
		portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );							\
	}																								\
}

如果对应优先级下就绪列表为空就复位优先级。因为FreeRTOS同一个优先级下可以有多个任务,所以要判断一下同优先级下是否还有其它就绪任务,不能盲目复位优先级。
3、if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )判断待删除任务的事件列表项是否被挂载到了列表中,如果挂载到了列表中,那么就说明此任务正在等待事件,将其从所在的列表中删除。
4、判断待删除的任务是否是当前任务。分两种情况:第一种情况,待删除的任务恰好是当前任务,这种情况比较麻烦,不能立即删除,因为当前任务还要运行(立即释放TCB和Stack会让MCU找不着北),所以就将任务的状态列表项添加到xTasksWaitingTermination列表中。xTasksWaitingTermination列表中挂载的是已删除但未释放内存的任务。在空闲任务中对此列表进行处理。uxDeletedTasksWaitingCleanUp变量记录的是当前系统中有多少个已被删除但未释放内存的任务。第二种情况,待删除的任务不是当前任务,这种情况简单,系统中任务个数减减,释放内存,复位下一个任务解锁时间就OK了。为什么要复位下一个任务解锁时间呢?原因就是如果删除的任务恰好是下一个解锁的任务,那么不复位任务解锁时间就会错把已删除任务的解锁时间给系统使用。
prvDeleteTCB释放内存函数的核心代码如下:

vPortFree( pxTCB->pxStack );//先释放任务堆栈
vPortFree( pxTCB );//再释放TCB

再来看复位下一个任务解锁时间函数。

static void prvResetNextTaskUnblockTime( void )
{
TCB_t *pxTCB;

	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 = 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. */
		//获取延时列表中第一个列表项所属的TCB
		( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
		//获取状态列表项的值,状态列表项的值表示的是延时的时间
		xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
	}
}

( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );获取延时列表中第一个列表项所属的TCB,为什么是第一个列表项?因为FreeRTOS的列表是从小到大排序的,那么最小的值肯定对应的是最近要解锁的任务。获取到要解锁任务的TCB以后,通过xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );把TCB中状态列表项的值赋值给xNextTaskUnblockTime。
5、如果调度器是运行的,并且删除的任务是正在运行的任务那么就进行一次任务调度。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值