FreeRTOS_第16章_任务管理_常用的任务函数讲解

简介:

本文是 [野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》 这本书第16章_任务管理_常用的任务函数讲解的补充

跟踪钩子宏:

traceTASK_SUSPEND( pxTCB );中以mt或者trace开头的函数是调试追踪函数,没用的时候使用宏定义不占用系统时间,下面是官方的一段描述,使用详情参考

Description

Trace hook macros are a very powerful feature that permit you to collect data on how your embedded application is behaving.

Key points of interest within the FreeRTOS source code contain empty macros that an application can re-define for the purpose of providing application specific trace facilities. The application need only implement those macros of particular interest – with unused macros remaining empty and therefore not impacting the application timing.

就绪\阻塞\悬挂任务节点简化图:

​ 图中所示的任务节点是TCB的xStateListItem就绪/阻塞/悬挂节点的接口,TCB还有一个xEventListItem事件的接口,两者非常类似,就是功能不一样,本文任务调度暂且不讲述事件接口,只关注就绪/阻塞/悬挂节点的接口。

​ 任务节点是一个双向链表,就绪/阻塞/悬挂大表的位置保存一个简化的任务节点作为最后节点,这个最后节点用于遍历。

​ 任务节点里面有一个pvOwner,可以指向自己的TCB用于查询其他信息,可以有优先级,栈的起始位置,栈顶的指针,TCB任务的名字,事件组等。

任务节点里面有一个pvContainer,可以指向自己的正在服务于的大表(就绪/阻塞/悬挂),并可查询该大表的拥有的任务节点数,和正在执行的TCB。

在这里插入图片描述

任务挂起函数

任务悬挂 vTaskSuspend( TaskHandle_t xTaskToSuspend )

流程图:

在这里插入图片描述

代码:

/**************************************************************************************************
* 函数:                                   vTaskSuspend
* 说明:  
* 输入:  
*		   TaskHandle_t xTaskToSuspend		挂起任务的句柄,NULL是挂起自己
* 关联变量: 				
*						pxCurrentTCB		当前任务TCB指针
*						xSchedulerRunning	调度器运行状态
*						xSuspendedTaskList	悬挂表
*						uxCurrentNumberOfTasks	当前运行任务数量
* 返回:  
* 注释作者:  日南方  11/27/2020
**************************************************************************************************/
#if ( INCLUDE_vTaskSuspend == 1 )
	void vTaskSuspend( TaskHandle_t xTaskToSuspend )
	{
		TCB_t *pxTCB;
		//进入临界区
		taskENTER_CRITICAL();
		{
			/* If null is passed in here then it is the running task that is
			being suspended. */
			//通过句柄获取要悬挂的任务块句柄,就NULL特殊为当前任务句柄
			pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
			
			//这个是追踪调试函数,目前没用宏定义
			traceTASK_SUSPEND( pxTCB );

			/* Remove task from the ready/delayed list and place in the
			suspended 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? */
			//查看任务的 事件的结点,并从对应的事件列表中脱钩
			//(一开始这里可能不知道什么是事件列表,就先把他理解一个阻塞列表,在20章的时候会介绍)
			if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
			{
				( void ) uxListRemove( &( pxTCB->xEventListItem ) );
			}
			else
			{
				//调试追踪函数
				mtCOVERAGE_TEST_MARKER();
			}
			
			//将就绪/阻塞/悬挂的结点插入到悬挂表
			vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
		}
		//结束临界区
		taskEXIT_CRITICAL();

		if( xSchedulerRunning != pdFALSE )
		{
			//如果正在运行,刚好下一个阻塞任务就是刚刚悬挂的任务,
			//则延时时间到了,任务没了,则会出错
			//所以更新解锁时钟,将还在阻塞任务列表中的下一个解锁时间更新一下
			/* Reset the next expected unblock time in case it referred to the
			task that is now in the Suspended state. */
			taskENTER_CRITICAL();
			{
				//更新解锁时钟
				prvResetNextTaskUnblockTime();
			}
			taskEXIT_CRITICAL();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		
		//如果是当前运行任务
		if( pxTCB == pxCurrentTCB )
		{
			if( xSchedulerRunning != pdFALSE )
			{
				//调度器在运行
				/* The current task has just been suspended. */
				configASSERT( uxSchedulerSuspended == 0 );
				//如果悬挂任务是当前任务,立即切换任务
				portYIELD_WITHIN_API();
			}
			else
			{
				//调度器未运行(xSchedulerRunning == pdFALSE ),
				//但 pxCurrentTCB 指向的任务刚刚被暂停,
				//所以必须调整 pxCurrentTCB 以指向其他任务。
				//首先调用函数 listCURRENT_LIST_LENGTH()
				//判断一下系统中所有的任务是不是都被挂起了,
				//也就是查看列表 xSuspendedTaskList 的长度是不是等于 uxCurrentNumberOfTasks,
				//事实上并不会发生这种情况,因为空闲任务是不允许被挂起和阻塞的,必须保证系统中无论如何都有一个任务可以运行

				/* The scheduler is not running, but the task that was pointed
				to by pxCurrentTCB has just been suspended and pxCurrentTCB
				must be adjusted to point to a different task. */
				if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
				{
					//悬挂任务总数等于当前任务总数,即无就绪任务
					/* No other tasks are ready, so set pxCurrentTCB back to
					NULL so when the next task is created pxCurrentTCB will
					be set to point to it no matter what its relative priority
					is. */
					pxCurrentTCB = NULL;
				}
				else
				{
					//有其他任务,就切到其他任务
					vTaskSwitchContext();
				}
			}
		}
		else
		{
			//调试追踪任务
			mtCOVERAGE_TEST_MARKER();
		}
	}

#endif /* INCLUDE_vTaskSuspend */

任务全部悬挂 vTaskSuspendAll( void )

删除任务节点函数:

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 = ( List_t * ) pxItemToRemove->pvContainer;

	//当前任务节点脱钩,把任务节点前面的连接到任务节点后面去
	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->pvContainer = NULL;
	( pxList->uxNumberOfItems )--;

	//返回个数
	return pxList->uxNumberOfItems;
}

任务恢复函数

代码详情xTaskResumeAll()

/**************************************************************************************************
* 函数:                                   xTaskResumeAll
* 说明:   恢复被挂起的调度器
* 输入:  
*		   
* 关联变量: 
* 返回:  
* 注释作者:  日南方  12/4/2020
**************************************************************************************************/
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;

	/* If uxSchedulerSuspended is zero then this function does not match a
	previous call to vTaskSuspendAll(). */
	//如果 uxSchedulerSuspended 为 0,
	//则此函数与先前对 vTaskSuspendAll()的调用不匹配,
	//不需要调用 xTaskResumeAll()恢复调度器。
	configASSERT( uxSchedulerSuspended );

	/* It is possible that an ISR caused a task to be removed from an event
	list while the scheduler was suspended.  If this was the case then the
	removed task will have been added to the xPendingReadyList.  Once the
	scheduler has been resumed it is safe to move all the pending ready
	tasks from this list into their appropriate ready list. */
	//进入临界区
	taskENTER_CRITICAL();
	{
	 	//我们知道,每调用一次vTaskSuspendAll()函数就会将变量加一,那么调用对应的xTaskResumeAll()肯定就是将变量减一。
		--uxSchedulerSuspended;

		//如果调度器恢复正常工作,也就是调度器没有被挂起,就可以将所有待处理的就绪任务从待处理就绪列表 xPendingReadyList 移动到适当的就绪列表中。
		if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
		{
			if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
			{
				/* Move any readied tasks from the pending list into the
				appropriate ready list. */
				//将任何准备好的任务从待处理就绪列表移动到相应的就绪列表中。
				当待处理就绪列表(悬挂表) xPendingReadyList 中是非空的时候,
				就需要将待处理就绪列表中(悬挂表)的任务移除,添加到就绪列表中去。
				while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
				{
					//将悬挂表的任务节点摘出来挂到就绪表中
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
					( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
					prvAddTaskToReadyList( pxTCB );

					/* If the moved task has a priority higher than the current
					task then a yield must be performed. */
					//如果移动的任务的优先级高于当前任务,需要进行一次任务的切换
					//xYieldPending = pdTRUE 表示需要进行任务切换
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
					{
						//yield产生   + pending未决事情 = 产生待决申请操作 = 调度信号
						xYieldPending = pdTRUE;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				if( pxTCB != NULL )
				{
					/* A task was unblocked while the scheduler was suspended,
					which may have prevented the next unblock time from being
					re-calculated, in which case re-calculate it now.  Mainly
					important for low power tickless implementations, where
					this can prevent an unnecessary exit from low power
					state. */
					//在调度器被挂起时,任务被解除阻塞,这可能阻止了重新计算下一个解除阻塞时间,
                    //在这种情况下,重置下一个任务的解除阻塞时间
					prvResetNextTaskUnblockTime();
				}

				/* If any ticks occurred while the scheduler was suspended then
				they should be processed now.  This ensures the tick count does
				not	slip, and that any delayed tasks are resumed at the correct
				time. */
				//如果在调度器挂起这段时间产生滴答定时器的计时并且在这段时间有任务解除阻塞,
				//由于调度器的挂起导致没法切换任务,当恢复调度器的时候应立即处理这些任务。
				//这样确保了滴答定时器的计数不会滑动,并且任何在延时的任务都会在正确的时间恢复。 
				{
					//调度器挂起期间产生的滴答时钟uxPendedTicks
					UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */

					if( uxPendedCounts > ( UBaseType_t ) 0U )
					{
						do
						{
							//有任务接触阻塞
							if( xTaskIncrementTick() != pdFALSE )
							{
								//尝试调度信号
								xYieldPending = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
							--uxPendedCounts;
						} while( uxPendedCounts > ( UBaseType_t ) 0U );

						uxPendedTicks = 0;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				if( xYieldPending != pdFALSE )
				{
                    //如果是抢占式内核
					#if( configUSE_PREEMPTION != 0 )
					{
						xAlreadyYielded = pdTRUE;
					}
					#endif
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	taskEXIT_CRITICAL();

	return xAlreadyYielded;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值