本节描述任务相关的控制。
主要讲解使用,源码分析后面对应章节会有。
学习本节前,建议同学们往前回忆下任务控制块的内容。
参考:
任务控制主要是对任务控制块的处理。
比如任务延时、重置任务优先级、任务挂起与恢复。
对于延时相关的代码细节,可以参考前面的【freertos】007-系统节拍和系统延时管理实现细节章节详细分析。
void vTaskDelay( portTickTypexTicksToDelay );vTaskDelay()用于相对延时,是指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束。xTicksToDelay参数用于设置延迟的时钟节拍个数。#define portMAX_DELAY (TickType_t )0xffffffffUL
static void lzmTestTask(void* parameter){ /* task init */ printf("start lzmTestTask\r\n"); for(;;) { /* 任务主体 */ /* 延时1000个tick再跑 */ vTaskDelay(1000); }}该功能可用于周期性任务,保证执行频率不变。
BaseType_t vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement ); vTaskDelayUntil()用于绝对延时,也叫周期性延时。想象下精度不高的定时器。pxPreviousWakeTime参数是存储任务上次处于非阻塞状态时刻的变量地址。xTimeIncrement参数用于设置周期性延时的时钟节拍个数。pdFALSE 说明延时失败。#defineINCLUDE_vTaskDelayUntil 1
static void lzmTestTask(void* parameter){ portTickType last_wake_time = 0; /* task init */ printf("start lzmTestTask\r\n"); /* 重置下该变量 */ last_wake_time = xTaskGetTickCount(); for(;;) { /* 再确保任务主体占用CPU时长不会超过周期值(1000tick)的情况下, 不管任务主体跑多长时间,1000tick后依然内跑回这里。 */ vTaskDelayUntil(&last_wake_time, 1000); /* 任务主体 */ }}UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );xTask参数为任务句柄。传入NULL,表示获取当前调用该API的任务的优先级。INCLUDE_vTaskPriorityGet为1。UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ){ TCB_t const * pxTCB; UBaseType_t uxReturn; taskENTER_CRITICAL(); /* 加入临界 */ { /* 获取任务控制块 */ pxTCB = prvGetTCBFromHandle( xTask ); /* 通过任务控制块获取任务优先级 */ uxReturn = pxTCB->uxPriority; } taskEXIT_CRITICAL(); /* 退出临界 */ return uxReturn;} void vAFunction( void ) { TaskHandle_t xHandle; /* 创建一个任务,存储该句柄 */ xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); // ... /* 使用句柄获取创建的任务的优先级 */ if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ) { /* 任务可以改变自己的优先级 */ } // ... /* 当前任务优先级比创建的任务优先级高? */ if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ) { /* 当前优先级较高 */ } }任务优先级除了在创建时设置外,也可以在系统启动后重置,毕竟任务优先级的本质也只是任务控制块里面的一直成员值。
但是修改优先级时需要维护优先级继承机制。
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );作用:
参数:
xTask:需要修改任务优先级的任务句柄。NULL时,表示修改当前任务的任务优先级。uxNewPriority:新的任务优先级。在[0,configMAX_PRIORITIES - 1]范围内,否则会引起断言。使能方法:使用该功能需要在FreeRTOSConfig.h中配置INCLUDE_vTaskPrioritySet为1。
更改任务优先级的实现,是更改任务控制块里面记录的任务优先级值,但是需要维护好优先级继承机制。
看到了源码,产生两个疑问:
重置优先级简要步骤:
传入的优先级必须小于限制值,否则会触发断言。
/* 断言式参数校验 */configASSERT( uxNewPriority < configMAX_PRIORITIES );/* 参数纠正 */if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){ uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;}重置任务优先级,涉及到就绪链表、事件链表的操作,而系统时钟节拍这些中断会设计到操作这些链表。
通过任务句柄获取任务控制块,通过任务控制块获取任务优先级。
如果使能了互斥量,及系统支持优先级继承机制时,需要区分基优先级uxBasePriority和在用优先级uxPriority。
/* 获取任务控制块 */pxTCB = prvGetTCBFromHandle( xTask );#if ( configUSE_MUTEXES == 1 ){ /* 开启了互斥量就获取基优先级,处理优先级继承使用 */ uxCurrentBasePriority = pxTCB->uxBasePriority;}#else{ /* 没有开启互斥量功能就直接获取优先级 */ uxCurrentBasePriority = pxTCB->uxPriority;}#endif在修改任务优先级前,先检查修改后是否需要进行任务调度,以下情况都需要任务调度:
实现代码如下:
/* 检查是否需要标记任务调度 */if( uxNewPriority > uxCurrentBasePriority ) /* 新的优先级比基优先级更高了 */{ if( pxTCB != pxCurrentTCB ) { /* 如果需要修改的任务不是当前在跑任务,且新配置的优先级大于当前在跑的任务优先级,需要标记任务调度 */ if( uxNewPriority >= pxCurrentTCB->uxPriority ) { /* 标记任务调度 */ xYieldRequired = pdTRUE; } } else { /* 如果被提高优先级的任务已经在跑了,就不需要任务切换 */ }}else if( pxTCB == pxCurrentTCB ) /* 把当前任务优先级下调,也需要触发任务调度 */{ /* 标记任务调度 */ xYieldRequired = pdTRUE;}else{ /* 下调其它任务优先级,不需要调度 */}在更新任务优先级前,需要保存该任务在用优先级,等等用于迁移就绪链表。
/* 获取该任务当前使用的优先级 */uxPriorityUsedOnEntry = pxTCB->uxPriority;如果开启了互斥量功能,检查该任务是否处于优先级继承状态:
如果是,则不更新该任务在用优先级值。
如果不是,则需要更新该任务在用优先级值。
#if ( configUSE_MUTEXES == 1 ){ /* 开启了互斥量功能,但是当前没有在优先级继承状态,可以更新当前任务在用优先级 */ if( pxTCB->uxBasePriority == pxTCB->uxPriority ) { pxTCB->uxPriority = uxNewPriority; } /* 更新基优先级 */ pxTCB->uxBasePriority = uxNewPriority;}#else /* if ( configUSE_MUTEXES == 1 ) */{ /* 没有开启互斥量功能就直接更新当前在用优先级 */ pxTCB->uxPriority = uxNewPriority;}#endif /* if ( configUSE_MUTEXES == 1 ) */按照作者的想法,任务优先级会影响到该任务在事件链表中的位置,所以也需要对事件链表处理。
由于事件链表节点值按功能装载不同的值:
所以修改该值前先判断当前是否装载任务优先级。
/* 当前事件链表节点值是否被锁定。参考freertos事件组组件 */if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ){ /* 时间链表节点值没有被锁定,则默认用于保存任务优先级,用于事件链表排序。可更新事件链表节点值。 */ listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );}当前freertos官方提供的修改任务优先级API内事件链表处理代码就这。
按照作者的想法,如果更新了任务优先级到事件节点值。
也应该检查下当前任务是否阻塞在有序事件链表中,如消息队列,这些都是按照优先级插入事件链表的,解除阻塞是取应该排序在前的任务的。
如果被修改任务优先级的任务在就绪链表,需要迁移到新的优先级就绪链表中。
该任务如果处于就绪态,会存在在用优先级的就绪链表中,而不是基优先级的就绪链表。
迁移就绪链表时需要注意,如果迁出就绪链表后,该链表没有就绪任务了,需要对系统任务优先级位图值uxTopReadyPriority进行更新处理。
uxTopReadyPriority该值是一个位图值。uxTopReadyPriority该值就是系统就绪任务中最高优先级的值。所以实现代码如下:
/* 判断被调节优先级的任务是否处于就绪态,如果是,需要迁移到新的优先级的就绪链表。 */if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ){ /* 解除任务所有状态,即是迁出当前就绪链表。 */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* 如果当前就绪链表没有其它任务了,迁出就绪任务优先级位图值对应位。 */ portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); } /* 根据新的优先级重新插入就绪链表 */ prvAddTaskToReadyList( pxTCB );}所有功能都实现后,触发任务调度,退出临界后,便可进入调度异常的回调进行任务调度。
void vAFunction( void ){ TaskHandle_t xHandle; /* 创建一个任务,存储该句柄 */ xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); // ... /* 使用句柄重置创建任务的优先级 */ vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 ); // ... /* 传入null,重置当前任务优先级 */ vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );}void vTaskSuspend( TaskHandle_t xTaskToSuspend );参数:xTaskToSuspend:需要挂起的任务的任务句柄。为NULL时,挂起当前任务。
使能方法:在FreeRTOSConfig.h中配置INCLUDE_vTaskSuspend为1。
作用:挂起一个任务。任务挂起后,插入到就挂起链表中,该任务不会被调度,也无权占用CPU。
配对使用API:调用vTaskResume()恢复被挂起的任务到就绪链表。
挂起任务的处理设计到任务状态链表和任务解除阻塞时间这些全局数据,而这些数据在滴答时钟或者其它中断回调中使用的后缀FromISR API中也可能用到。
所以为了维护这些数据的原子性,需要使用临界级别来实现。
进出临界使用的函数:
/* 进入临界 */taskENTER_CRITICAL()/* 退出临界 */taskEXIT_CRITICAL()/* 获取需要挂起的任务句柄。传入NULL,即获取当前任务的句柄。 */pxTCB = prvGetTCBFromHandle( xTaskToSuspend );切换任务状态不是设置某个任务状态值,而是把任务按规则放到各种状态链表。
/* 解除任务所有状态。即是把任务从状态链表中迁出。 */if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){ /* 移出后如果当前优先级的就绪链表没有其它任务了,就需要重置下位图标。(开启了优先级优化功能才会生效) */ taskRESET_READY_PRIORITY( pxTCB->uxPriority );}if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){ /* 如果存在事件,需要从事件中移除。 */ ( void ) uxListRemove( &( pxTCB->xEventListItem ) );}/* 把任务插入到挂起链表 */vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );#if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任务通知功能 */{ BaseType_t x; for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) { if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) { /* 如果任务正在等待任务通知,则当任务被挂起时,需要清除这些任务通知。 */ pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; } }}#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */完成以上四小步才算是把任务从其它状态切入到挂起态(是挂起任务的挂起态)。
为了防止挂起的任务是下一个需要解除阻塞的任务而导致系统提前进入检索解除阻塞任务的多余操作,这里可以刷新下解除阻塞任务的时间。
if( xSchedulerRunning != pdFALSE ) /* 调度器已经启动了 */{ taskENTER_CRITICAL(); { /* 如果调度器已经开启了,需要更新下一个需要解除任务阻塞的时间 */ prvResetNextTaskUnblockTime(); } taskEXIT_CRITICAL();}如果挂起的任务是当前任务,那需要更新下pxCurrentTCB值。
如果调度器已经启动了,挂起当前任务后,需要强制触发任务调度。
如果调度器还没有启动,挂起了当前任务,就需要更新pxCurrentTCB值即可。等待调度器启动后先跑pxCurrentTCB。
如果全部任务都被挂起了,就设置pxCurrentTCB为空即可。下次创建任务或者恢复任务时会重置pxCurrentTCB。至少会在启动调度器时会创建空闲任务,所以在启动调度器前不必在乎pxCurrentTCB值是否为空。
如果不是全部任务都被挂起,那就从就绪表中选出最合适的任务到pxCurrentTCB。
vTaskSwitchContext(),该任务的分析可以往前面的任务切换章节翻。if( pxTCB == pxCurrentTCB ) /* 挂起当前任务 */{ if( xSchedulerRunning != pdFALSE ) { /* 调度器正常运行,需要强制触发任务调度,把任务切走 */ configASSERT( uxSchedulerSuspended == 0 ); portYIELD_WITHIN_API(); } else { if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ { /* 如果所有任务都被挂起了,就把pxCurrentTCB值标记为空 */ pxCurrentTCB = NULL; } else { /* 找出新的pxCurrentTCB值 */ vTaskSwitchContext(); } }}void vTaskResume( TaskHandle_t xTaskToResume );xTaskToResume:需要解除挂起的任务句柄。
INCLUDE_vTaskSuspend必须定义为vTaskSuspend() 1,这个函数才生效。
该函数用于解除挂起的任务。
被一个或多个vTaskSuspend()调用挂起的任务将通过对vTaskResume()的单个调用重新可用。
解除任务的挂起态的实现比较简单,主要思路:
vTaskResume():
#if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */void vTaskResume( TaskHandle_t xTaskToResume ){ TCB_t * const pxTCB = xTaskToResume; /* 任务句柄不能为NULL */ configASSERT( xTaskToResume ); /* 正在跑的任务在运行态,不用处理。 */ if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) { taskENTER_CRITICAL(); /* 进入临界。因为下面操作涉及任务状态表等系统相关的全局变量。 */ { if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) /* 如果该任务处于挂起态 */ { /* 从挂起链表迁出 */ ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); /* 重新插入到就绪链表 */ prvAddTaskToReadyList( pxTCB ); /* 如果恢复的任务的优先级更高,就触发任务调度。 */ if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) { /* 触发任务调度 */ taskYIELD_IF_USING_PREEMPTION(); } } } taskEXIT_CRITICAL(); /* 退出临界 */ } else { mtCOVERAGE_TEST_MARKER(); }}#endif /* INCLUDE_vTaskSuspend */prvTaskIsTaskSuspended():
#if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ){ BaseType_t xReturn = pdFALSE; const TCB_t * const pxTCB = xTask; /* 访问xPendingReadyList,因此必须从临界区调用。所以需要在调用本函数前进入。 */ /* 检查在跑任务是否挂起是没有意义的 */ configASSERT( xTask ); /* 检查该任务的状态 */ if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) /* 该任务挂载在挂起链表 */ { if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE )/* 该任务不是因为调度器挂起而暂时放到挂起链表的 */ { /* 再判断该任务是否是因为等待事件而永久阻塞的,如果是,也不属于挂起态。 */ if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) { xReturn = pdTRUE; } } } return xReturn;}#endif /* INCLUDE_vTaskSuspend */void vAFunction( void ){ TaskHandle_t xHandle; /* 创建一个任务,存储该句柄 */ xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); // ... /* 挂起这个刚创建的任务 */ vTaskSuspend( xHandle ); // ... /* 挂起当前在跑任务 */ vTaskSuspend( NULL ); /* 在被其它任务恢复当前任务前,是不会跑到这里的 */}void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ){ TCB_t * pxTCB; UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; BaseType_t xYieldRequired = pdFALSE; /* 断言式参数校验 */ configASSERT( uxNewPriority < configMAX_PRIORITIES ); /* 参数纠正 */ if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) { uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } else { mtCOVERAGE_TEST_MARKER(); } /* 进入临界处理 */ taskENTER_CRITICAL(); { /* 获取任务控制块 */ pxTCB = prvGetTCBFromHandle( xTask ); traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); #if ( configUSE_MUTEXES == 1 ) { /* 开启了互斥量就获取基优先级,处理优先级继承使用 */ uxCurrentBasePriority = pxTCB->uxBasePriority; } #else { /* 没有开启互斥量功能就直接获取优先级 */ uxCurrentBasePriority = pxTCB->uxPriority; } #endif /* 新配置的优先级和原有的优先级不一样才会处理 */ if( uxCurrentBasePriority != uxNewPriority ) { /* 检查是否需要标记任务调度 */ if( uxNewPriority > uxCurrentBasePriority ) /* 新的优先级比基优先级更高了 */ { if( pxTCB != pxCurrentTCB ) { /* 如果需要修改的任务不是当前在跑任务,且新配置的优先级大于当前在跑的任务优先级,需要标记任务调度 */ if( uxNewPriority >= pxCurrentTCB->uxPriority ) { /* 标记任务调度 */ xYieldRequired = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } else { /* 如果被提高优先级的任务已经在跑了,就不需要任务切换 */ } } else if( pxTCB == pxCurrentTCB ) /* 把当前任务优先级下调,也需要触发任务调度 */ { /* 标记任务调度 */ xYieldRequired = pdTRUE; } else { /* 下调其它任务优先级,不需要调度 */ } /* 记录该任务当前使用的优先级 */ uxPriorityUsedOnEntry = pxTCB->uxPriority; #if ( configUSE_MUTEXES == 1 ) { /* 开启了互斥量功能,但是当前没有在优先级继承状态,可以更新当前任务在用优先级 */ if( pxTCB->uxBasePriority == pxTCB->uxPriority ) { pxTCB->uxPriority = uxNewPriority; } else { mtCOVERAGE_TEST_MARKER(); } /* 更新基优先级 */ pxTCB->uxBasePriority = uxNewPriority; } #else /* if ( configUSE_MUTEXES == 1 ) */ { /* 没有开启互斥量功能就直接更新当前在用优先级 */ pxTCB->uxPriority = uxNewPriority; } #endif /* if ( configUSE_MUTEXES == 1 ) */ /* 当前事件链表节点值是否被锁定。参考freertos事件组组件 */ if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) { /* 时间链表节点值没有被锁定,则默认用于保存任务优先级,用于事件链表排序。可更新事件链表节点值。 */ listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } else { mtCOVERAGE_TEST_MARKER(); } /* 判断被调节优先级的任务是否处于就绪态,如果是,需要迁移到新的优先级的就绪链表。 */ if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) { /* 解除任务所有状态,即是迁出当前就绪链表。 */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* 如果当前就绪链表没有其它任务了,迁出就绪任务优先级位图值对应位。 */ portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); } else { mtCOVERAGE_TEST_MARKER(); } /* 根据新的优先级重新插入就绪链表 */ prvAddTaskToReadyList( pxTCB ); } else { mtCOVERAGE_TEST_MARKER(); } if( xYieldRequired != pdFALSE ) { /* 触发任务调度 */ taskYIELD_IF_USING_PREEMPTION(); } else { mtCOVERAGE_TEST_MARKER(); } /* 编译警告处理 */ ( void ) uxPriorityUsedOnEntry; } } /* 退出临界 */ taskEXIT_CRITICAL();}#if ( INCLUDE_vTaskSuspend == 1 )void vTaskSuspend( TaskHandle_t xTaskToSuspend ){ TCB_t * pxTCB; taskENTER_CRITICAL(); { /* 获取需要挂起的任务句柄。传入NULL,即获取当前任务的句柄。 */ pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); traceTASK_SUSPEND( pxTCB ); /* 解除任务所有状态。即是把任务从状态链表中迁出。 */ 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(); } /* 把任务插入到挂起链表 */ vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); #if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任务通知功能 */ { BaseType_t x; for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) { if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) { /* 如果任务正在等待任务通知,则当任务被挂起时,需要清除这些任务通知。 */ pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; } } } #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ } taskEXIT_CRITICAL(); if( xSchedulerRunning != pdFALSE ) { taskENTER_CRITICAL(); { /* 如果调度器已经开启了,需要更新下一个需要解除任务阻塞的时间 */ prvResetNextTaskUnblockTime(); } taskEXIT_CRITICAL(); } else { mtCOVERAGE_TEST_MARKER(); } if( pxTCB == pxCurrentTCB ) /* 挂起当前任务 */ { if( xSchedulerRunning != pdFALSE ) { /* 调度器正常运行,需要强制触发任务调度,把任务切走 */ configASSERT( uxSchedulerSuspended == 0 ); portYIELD_WITHIN_API(); } else { if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ { /* 如果所有任务都被挂起了,就把pxCurrentTCB值标记为空 */ pxCurrentTCB = NULL; } else { /* 找出新的pxCurrentTCB值 */ vTaskSwitchContext(); } } } else { mtCOVERAGE_TEST_MARKER(); }}#endif /* INCLUDE_vTaskSuspend */