任务调度器的挂起和恢复
挂起任务调度器,调用此函数不需要关闭中断
使用格式示例:
1.与临界区不一样的是,挂起任务调度器,未关闭中断;
2.它仅仅是防止;饿任务之间的资源争夺,中断照样可以直接响应;
3.挂起调度器的方式,适合于临界区位于任务与任务之间;既不用去延时中断,又可以做临界区的安全。
vTaskSuspendAll()任务调度器挂起函数详解
void vTaskSuspendAll( void )
{
/* A critical section is not required as the variable is of type
BaseType_t. Please read Richard Barry's reply in the following link to a
post in the FreeRTOS support forum before reporting this as a bug! -
http://goo.gl/wu4acr */
/*对变量加1,其实任务调度就是任务切换,任务切换又PendSV中断实现中断,进行任务切换*/
++uxSchedulerSuspended;
}
PendSV的触发是在SysTick定时器中断里面触发的, xPortSysTickHandler()。
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
executes all interrupts must be unmasked. There is therefore no need to
save and then restore the interrupt mask value as its value is already
known - therefore the slightly faster vPortRaiseBASEPRI() function is used
in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
/*将BASEPRI设置为最大系统调用优先级,以实现临界区*/
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
/*返回的是pdFALSE所以不会触发PendSV中断不会任务切换,认为是调度器挂起了*/
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */
/*触发PendSV中断*/
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR();
}
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
/* Called by the portable layer each time a tick interrupt occurs.
Increments the tick then checks to see if the new tick value will cause any
tasks to be unblocked. */
/*为实现*/
traceTASK_INCREMENT_TICK( xTickCount );
/*判断变量,此变量在调用挂起任务调度器时会加1,所以不回进入到if里面*/
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* Minor optimisation. The tick count cannot change in this
block. */
const TickType_t xConstTickCount = xTickCount + 1;
/* Increment the RTOS tick, switching the delayed and overflowed
delayed lists if it wraps to 0. */
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U )
{
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* See if this tick has made a timeout expire. Tasks are stored in
the queue in the order of their wake time - meaning once one task
has been found whose block time has not expired there is no need to
look any further down the list. */
if( xConstTickCount >= xNextTaskUnblockTime )
{
for( ;; )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/* The delayed list is empty. Set xNextTaskUnblockTime
to the maximum possible value so it is extremely
unlikely that the
if( xTickCount >= xNextTaskUnblockTime ) test will pass
next time through. */
xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
break;
}
else
{
/* The 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 must
be removed from the Blocked state. */
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
if( xConstTickCount < xItemValue )
{
/* It is not time to unblock this item yet, but the
item value is the time at which the task at the head
of the blocked list must be removed from the Blocked
state - so record the item value in
xNextTaskUnblockTime. */
xNextTaskUnblockTime = xItemValue;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* It is time to remove the item from the Blocked state. */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* Is the task waiting on an event also? If so remove
it from the event list. */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Place the unblocked task into the appropriate ready
list. */
prvAddTaskToReadyList( pxTCB );
/* A task being unblocked cannot cause an immediate
context switch if preemption is turned off. */
#if ( configUSE_PREEMPTION == 1 )
{
/* Preemption is on, but a context switch should
only be performed if the unblocked task has a
priority that is equal to or higher than the
currently executing task. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
/* Tasks of equal priority to the currently running task will share
processing time (time slice) if preemption is on, and the application
writer has not explicitly turned time slicing off. */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
#if ( configUSE_TICK_HOOK == 1 )
{
/* Guard against the tick hook being called when the pended tick
count is being unwound (when the scheduler is being unlocked). */
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICK_HOOK */
}
/*挂起任务调度器进入此处*/
else
{
++uxPendedTicks;
/* The tick hook gets called at regular intervals, even if the
scheduler is locked. */
#if ( configUSE_TICK_HOOK == 1 )
{
vApplicationTickHook();
}
#endif
}
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
/*返回0*/
return xSwitchRequired;
}
总结:
vTaskSuspendAll(),调用一次挂起调度器,该变量uxSchedulerSuspended就加一,变量uxSchedulerSuspended的值不为0,将会导致Systick无法触发PendSV中断,即挂起调度器
xTaskResumeAll()任务调度器挂起函数详解
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(). */
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();
{
/*变量减1,挂起是把此变量加1,恢复是把这个变量减1*/
--uxSchedulerSuspended;
/*如果这个变量等于pdFALSE(0)的时候,代表调度器恢复了*/
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/*判断创建任务的总数是不是大于0*/
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
{
/* Move any readied tasks from the pending list into the
appropriate ready list. */
/*判断等待就绪列表是否有任务,有的话执行while*/
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. */
/*判断恢复的任务优先级是否大于当前的任务*/
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
/*执行任务切换*/
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. */
{
/*恢复滴答定时器,被挂起时丢失的任务数*/
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();
}
}
/*xYieldPending不等于pdFALSE代表需要任务切换*/
if( xYieldPending != pdFALSE )
{
/*不等于0代表使用抢占式调度*/
#if( configUSE_PREEMPTION != 0 )
{
/*变量赋值*/
xAlreadyYielded = pdTRUE;
}
#endif
/*执行任务切换*/
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/*退出临界区*/
taskEXIT_CRITICAL();
return xAlreadyYielded;
}
总结:
xTaskResumeAll(),调用一次恢复调度器,该变量uxSchedulerSuspended就减一,如果等于0,则允许调度:
1. 当任务数量大于0时,恢复调度器才有意义,如果没有一个已创建的任务就无意义
2.移除等待就绪列表中的列表项,恢复至就绪列表,直到xPendingReadyList列表为空
3.如果恢复的任务优先级比当前正在执行任务优先级更高,则将xYieldPending赋值为pdTRUE,表示需要进行一次任务切换
4.在调度器被挂起的期间内,是否有丢失未处理的滴答数。 xPendedCounts是丢失的滴答数,有则调用xTasklncrementTickf )补齐弄失的滴答数
5.判断是否允许任务切换
6.返回任务是否已经切换;已经切换返回pdTRUE;反之返回pdFALSE