FreeRTOS: vTaskSuspendAll xTaskResumeAll详解
当需要处理某些全局变量(数据)的时候,为了防止被同时多个线程修改引起问题,需要把线程抢占功能暂时屏蔽,需要用到vTaskSuspendAll()xTaskResumeAll()
void vTaskSuspendAll( void ):设置关闭线程调度的标志位
代码详解:
void vTaskSuspendAll( void )
{
//系统维护一个计数uxSchedulerSuspended,当它大于0时候表示禁止调度,等于0则打开调度(允许调度
++uxSchedulerSuspended;
}
void xTaskResumeAll( void ):恢复线程调度,且处理期间的数据
代码详解:
1,如果关闭调度的时候,有等候的线程(pending )需要把线程转移到就绪列表中prvAddTaskToReadyList.
2,如果关闭调度的时候,经过的多个Tick,需要执行对应的次数的xTaskIncrementTick.
3,如果以上都没有发生,继续执行原有的线程.
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;
//屏蔽中断.
taskENTER_CRITICAL();
{
// 将计数减一
--uxSchedulerSuspended;
// 如果等于0,则允许调度
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
//线程数量大于0,才可能进行调度
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
{
/* pending 中的线程数量(停止线程调度时候,积攒下来的线程数) > 0 */
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
{
/*将所有在xPendingReadyList中的任务移到对应的就绪链表中 */
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/* 如果pending中的线程任务优先级大于当前线程的优先级.需要做调度线程 */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xYieldPending = pdTRUE;
}
}
if( pxTCB != NULL )
{
/* 获取下次需要唤醒线程的那一刻的时间.如果没有那就 portMAX_DELAY
如果有的话,把时间保存在xNextTaskUnblockTime中*/
prvResetNextTaskUnblockTime();
}
/* 如果在禁止调度期间,有时钟节拍中断发生,则我们把发生的次数记录在uxPendedTicks中,称为丢失的时钟节拍数;
我们在这里模拟uxMissedTicks次时钟节拍中断,也就是说调用uxPendedTicks次时钟节拍isr: vTaskIncrementTick()。
这样保证了所有任务的延时量不会出现偏差,它们将在正确的时间被唤醒*/ */
{
UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
if( uxPendedCounts > ( UBaseType_t ) 0U )
{
do
{
/*xTaskIncrementTick:详细的可看下面
1,重新增加xTickCount.
2,线程中是否存在等待调度的信息.
*/
if( xTaskIncrementTick() != pdFALSE )
{
//存在需要调度的线程
xYieldPending = pdTRUE;
}
--uxPendedCounts;
} while( uxPendedCounts > ( UBaseType_t ) 0U );
uxPendedTicks = 0;
}
}
//如果存在需要调度的线程
if( xYieldPending != pdFALSE )
{
#if( configUSE_PREEMPTION != 0 )
{
xAlreadyYielded = pdTRUE;
}
#endif
taskYIELD_IF_USING_PREEMPTION();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
//放开中断
taskEXIT_CRITICAL();
return xAlreadyYielded;
}
代码分析:
1,累加Tick数.
2,是否在等待线程中转移到就绪列表的时候,有高优先级的任务.如果有返回TRUE.
3,xYieldPending ==TRUE => 返回TRUE.
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
//是否进制调度
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* 保存累加后的,数据 */
const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
/* 如果累加后出现越界,需要对应相关信息 */
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U )
{
/* 越界后的信息调整.
1,记录越界次数xNumOfOverflows
2,交换越界信息 pxDelayedTaskList <->pxOverflowDelayedTaskList
*/
taskSWITCH_DELAYED_LISTS();
}
/* 当前TIck大于等于,需要唤醒的线程时间 */
if( xConstTickCount >= xNextTaskUnblockTime )
{
for( ;; )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/* 如果没有Delayed线程,不需要唤醒*/
xNextTaskUnblockTime = portMAX_DELAY;
break;
}
else
{
/* 从 pxDelayedTaskList 中获取,需要环形的线程 */
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
if( xConstTickCount < xItemValue )
{
/*如果 pxDelayedTaskList 中的第一个线程需要唤醒的时间大于当前时间,代表着还需要继续睡眠
且下一个唤醒的时间是线程的时间 且退出.
(因为pxDelayedTaskList 中的线程是根据唤醒的时间排序的)
*/
xNextTaskUnblockTime = xItemValue;
break;
}
/* 把休眠中的线程 从等候pxDelayedTaskList 列表中删除*/
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* 如果这个线程还有事件等待状态的话需要从,事件等待中删除. */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
/* 把线程转移到 就绪列表中 */
prvAddTaskToReadyList( pxTCB );
#if ( configUSE_PREEMPTION == 1 )
{
/* 如果唤醒的线程优先级高于当前线程需要调度一下. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xSwitchRequired = pdTRUE;
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
}
else
{
//如果是不可调度状态的话,需要把等待的TIck事件保存起来.等到恢复调度的时候,再处理.
++uxPendedTicks;
}
#if ( configUSE_PREEMPTION == 1 )
{
//如果需要调度返回TRUE.
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
return xSwitchRequired;
}