创建
对于用户而言,最常用的就是任务创建,接下来对xTaskCreate()函数进行详细的描述。
xTaskCreate
函数原型:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;
- pxTaskCode:task的执行函数。
- pcName:task的名字。
- usStackDepth:stack的深度,注意单位不是字节数,而是configTACK_DEPTH_TYPE,所以stack的大小是configTACK_DEPTH_TYPE*usStackDepth。
- pvParameters:传给task执行函数的参数。
- uxPriority:task优先级。越大优先级越高。
- pxCreatedTask:task返回的句柄。可以用来改变task优先级、删除task等。
函数末尾有PRIVILEGED_FUNCTION描述。可以看到函数的数据放到特定的section里。用于链接脚本指定存放位置。默认这些宏都是定义成空的。暂时不用关心。
#define PRIVILEGED_FUNCTION __attribute__( ( section( "privileged_functions" ) ) )
#define PRIVILEGED_DATA __attribute__( ( section( "privileged_data" ) ) )
#define FREERTOS_SYSTEM_CALL __attribute__( ( section( "freertos_system_calls" ) ) )
函数返回值表示创建成功或者失败。
函数内主要调用prvCreateTask和prvAddNewTaskToReadyList,分别表示预创建任务,创建成功后将任务放到就绪列表。下面就分别分析下这两个函数。
prvCreateTask
函数原型:
pxNewTCB = prvCreateTask( pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask );
函数参数都是xTaskCreate()传入的,不做重复介绍了。
portSTACK_GROWTH
首先会进行stack的增长方向判断。portSTACK_GROWTH > 0 栈向上生长。portSTACK_GROWTH < 0 栈向下生长。这个和硬件相关。需要根据目标平台提前定好。
#define portSTACK_GROWTH ( -1 )
代码中有这样一段注释。
/* If the stack grows down then allocate the stack then the TCB so the stack
* does not grow into the TCB. Likewise if the stack grows up then allocate
* the TCB then the stack. */
所以栈的方向影响着代码的走向。为什么栈的增长方向和tcb、stack空间申请的顺序有关呢?
一个潜在的共识条件是:默认内存高地址在上,低地址在下。
我们假设现在栈的生长方向向下。内存分为堆(heap)和栈(stack)。变量存放在stack里,内存申请的区域位于heap。
先申请stack(task参数-栈空间),再申请tcb。 谁先申请谁在heap的地址越小。这样tcb就在stack上面了。又由于栈是向下增长,因此不会覆盖到TCB部分。前提条件是谁先申请谁的地址就小(PC模拟满足这个规律),但实际上频繁的内存申请和释放,可能不一定是这个规律。但这样做并没有什么坏处。TCB是任务控制模块,不能被破坏,因此即使栈溢出了最好也不要影响到TCB。
相反如果栈向上增长,那么就要先申请TCB,再申请stack。
我们以栈向下生长为例。申请完stack和tcb后。
pxNewTCB->pxStack = pxStack;
TCB的pxStack指向了申请的stack,这样TCB就记录栈的位置了。
然后标记下是动态申请的。
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
prvInitialiseNewTask
上面已经申请了相关的内存,接下来就对task进行初始化。
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t * pxNewTCB,
const MemoryRegion_t * const xRegions )
参数基本也是xTaskCreate()传入的,只是多了pxNewTCB、xRegions(默认NULL,MPU使用)。我们对默认不开启的功能先不分析了。
将task的栈进行memset。在申请的时候已经memset了,为什么这里还需要呢。因为这里是将栈的内容变成0xa5。并不是清0。
#if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
看宏的定义就知道了,如果开启了栈溢出检测功能,如果栈被修改了,肯定就不是0xa5了,这样就知道栈有没有溢出了。
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) )
#define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1
#else
#define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0
#endif
又对栈的方向做判断,假设是向下增长。
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
pxTopOfStack是栈顶,所以是栈数组的尾部。然后再进行对齐操作,这样pxTopOfStack就指向了pxStack的最后。注意栈顶并不是栈的开始,而是栈的结束。
然后对task的name做存储。最大支持12字节,最后赋值'/0'。
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
/* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
* configMAX_TASK_NAME_LEN characters just in case the memory after the
* string is not accessible (extremely unlikely). */
if( pcName[ x ] == ( char ) 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* Ensure the name string is terminated in the case that the string length
* was greater or equal to configMAX_TASK_NAME_LEN. */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
如果设定的优先级超过最大值。那么优先级就等于最大值-1。
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
这里uxBasePriority我猜应该是给优先级反转使用的。
pxNewTCB->uxPriority = uxPriority;
#if ( configUSE_MUTEXES == 1 )
{
pxNewTCB->uxBasePriority = uxPriority;
}
初始化state和event链表。
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
设置列表项的owner。表示链表项属于谁。 一般都是属于TCB。
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
设置事件列表项的value值是最大优先级减去task 优先级。上文说过列表是按照升序的。优先级越大数字越大,value越小,越位于列表的前面,变量列表越早获取。这样就可以按照优先级处理了。
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
事件列表项的owner也是TCB。
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
pxPortInitialiseStack
把栈顶赋值给tcp变量。
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
既然上面已经知道栈顶位置了,为什么要再计算一次呢。因为栈顶的一部分空间需要预留给Thread。
pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
创建event和thread(这是PC模拟器的代码,MCU上不是这样)。
pxThreadState->pvYieldEvent = CreateEvent( NULL, /* Default security attributes. */
FALSE, /* Auto reset. */
FALSE, /* Start not signalled. */
NULL ); /* No name. */
/* Create the thread itself. */
pxThreadState->pvThread = CreateThread()
MCU:
找一个cortex M4的demo
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
/* Simulate the stack frame as it would be created by a context switch
* interrupt. */
/* Offset added to account for the way the MCU uses the stack on entry/exit
* of interrupts, and to ensure alignment. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
/* Save code space by skipping register initialisation. */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
/* A save method is being used that requires each task to maintain its
* own exec return value. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXC_RETURN;
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
堆栈是用来进行上下文切换的时候保存现场的,一般在创建好之后会先对其初始化,对某些寄存器赋初值,这些初值就保存在任务堆栈中。
1、寄存器xPSR的值为portINTIAL_XPSR,其值为0x01000000。xPSR是m4的一个内存寄存器。
2、寄存器pc初始化为任务函数pxCode。
3、寄存器LR初始化为函数prvTaskError。
4、跳过4个寄存器,R12 R3 R2 R1,这四个寄存器不初始化。
5、寄存器R0初始化为pvParameters。一般情况,函数调用会将R0~R3作为输入参数,R0也可用于返回结果。这里pvParameter是任务函数的参数,保存在R0寄存器。
6、保存EXC_RETURN的值,用于退出中断处理器应该处于什么状态。
7、跳过8个寄存器,R11 10 9 8 7 6 5 4。
初始化之后如下:
返回的是pxThreadState的地址作为新的栈顶地址。
return ( StackType_t * ) pxThreadState;
prvAddNewTaskToReadyList
task创建完毕后,再将task添加到就绪链表。
prvAddNewTaskToReadyList( pxNewTCB );
uxCurrentNumberOfTasks
当前的task个数。
pxCurrentTCB
当前的任务控制块。如果为NULL,表示当前没有任务,或者其他的任务都在挂起状态。
如果等于NULL,就把pxNewTCB赋值给pxCurrentTCB。
pxCurrentTCB = pxNewTCB;
如果是第一个任务, 要进行初步初始化。
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* This is the first task to be created so do the preliminary
* initialisation required. We will not recover if this call
* fails, but we will report the failure. */
prvInitialiseTaskLists();
}
prvInitialiseTaskLists
可以看到就是初始化一些列表。task的各个状态都有对应的一个list。任务间的状态转换其实就是list之间的相关移动。具体另行分析。
static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;
for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
{
vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
}
vListInitialise( &xDelayedTaskList1 );
vListInitialise( &xDelayedTaskList2 );
vListInitialise( &xPendingReadyList );
#if ( INCLUDE_vTaskDelete == 1 )
{
vListInitialise( &xTasksWaitingTermination );
}
#endif /* INCLUDE_vTaskDelete */
#if ( INCLUDE_vTaskSuspend == 1 )
{
vListInitialise( &xSuspendedTaskList );
}
#endif /* INCLUDE_vTaskSuspend */
/* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList
* using list2. */
pxDelayedTaskList = &xDelayedTaskList1;
pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
假设pxCurrentTCB不为NULL,说明之前已经创建过task了。这时再判断调度器有没有启动,如果没有启动,那么判断task的优先级。也就是一定要是最高优先级的任务得到运行。
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
这里要判断调度器有没有启动是因为,调度器如果已经工作,不能直接修改pxCurrentTCB,需要调度器在任务切换的时候再进行,因为任务切花的过程复杂。
prvAddTaskToReadyList
将task放到就绪列表尾部。并设置就绪标志位。这样就知道当前最高优先级的任务是哪个。具体情况另行文章分析。
#define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( ( uxPriority ), uxTopReadyPriority )
这样就完成了task的创建了。
xTaskCreateStatic
上面说的是动态创建函数,还有一种方式是静态创建函数。
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
所谓的动态和静态的区别主要就是,动态的堆栈、TCB是自动申请的。静态的堆栈、TCB是提前申请好的。可以从参数看出,需要指定堆栈、TCB指针和大小。一般是定义一个数组,然后将数组作为参数传递进来。其他并没有什么特殊之处。
示例代码:
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务堆栈
StackType_t StartTaskStack[START_STK_SIZE];
//任务控制块
StaticTask_t StartTaskTCB;
//任务句柄
TaskHandle_t StartTask_Handler;
//声明任务函数
void start_task(void *pvParameters);
/*在main函数当中直接创建*/
int main()
{
/******************一些系统初始化函数(略)*********************/
Sys_Init();
/**********************************************************/
/*创建任务start_task*/
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint32_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数,也就是下面的函数当中的pvParameters参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(StackType_t* )StartTaskStack, //任务堆栈
(StaticTask_t* )&StartTaskTCB); //任务控制块 /*开启任务调度*/
vTaskStartScheduler();
}
/*任务函数*/
void start_task(void *pvParameters){
/*任务函数主体*/
while(1){
LED=1;
delay(800);
LED=0;
}
}
任务创建已经在别的章节里阐述过了。
删除
#if ( INCLUDE_vTaskDelete == 1 )
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
TCB_t * pxTCB;
traceENTER_vTaskDelete( xTaskToDelete );
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the calling task that is
* being deleted. */
//如果传入的是NULL,则删除的是调用该接口的task。
//如果原理是如果传入的是NULL,那么就返回pxCurrentTCB,因为这个指向当前正在运行的任务。
//也就是调用删除接口的task。
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/* Remove task from the ready/delayed list. */
//将task从list里移除。这个任务可能在ready list,也可能在delay list。所以只需要从当前所属list里删除即可。
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
//如果删除任务后,所对应的list为空了。那么就需要检查下优先级对应的就绪列表
//里有没有列表项了。如果也没有了,那就需要清除优先级对应的bitmap,表示当前
//优先级已经没有就绪任务了。这个和task切换任务有关,另作文章分析。
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Is the task waiting on an event also? */
//检查task是否还在等在event,其实就是检查event列表项的pxContailer是不是为NULL。
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
//如果在等待event,那么就从list里移除。
( 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 the task is running (or yielding), we must add it to the
* termination list so that an idle task can delete it when it is
* no longer running. */
//如果当前task正在运行,我们需要将task添加到termination list.
if( taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ) != pdFALSE )
{
/* A running task or a task which is scheduled to yield is being
* deleted. This cannot complete when the task is still running
* on a core, 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. */
//将task放到termination list里。
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. */
//idle会使用这个变量,判断还有没有task需要删除了,也就是如果删除的task是正常运行的话,是不能直接删除的,资源的释放都是在idle里处理。
++uxDeletedTasksWaitingCleanUp;
/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
* portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
traceTASK_DELETE( pxTCB );
/* 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. */
#if ( configNUMBER_OF_CORES == 1 )
portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) );
#else
portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ pxTCB->xTaskRunState ] ) );
#endif
}
else
{
--uxCurrentNumberOfTasks;
traceTASK_DELETE( pxTCB );
/* Reset the next expected unblock time in case it referred to
* the task that has just been deleted. */
//重新计算一下还要多长时间执行下一个任务,因为有可能别的task参数了即将删除的task时间。
prvResetNextTaskUnblockTime();
}
}
#if ( configNUMBER_OF_CORES == 1 )
{
taskEXIT_CRITICAL();
/* If the task is not deleting itself, call prvDeleteTCB from outside of
* critical section. If a task deletes itself, prvDeleteTCB is called
* from prvCheckTasksWaitingTermination which is called from Idle task. */
//如果删除的不是正在运行的task,那么资源可以立马释放,不用在idle里释放。
if( pxTCB != pxCurrentTCB )
{
prvDeleteTCB( pxTCB );
}
/* 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();
}
}
}
//以下是多核环境,暂不分析。
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
/* If a running task is not deleting itself, call prvDeleteTCB. If a running
* task deletes itself, prvDeleteTCB is called from prvCheckTasksWaitingTermination
* which is called from Idle task. */
if( pxTCB->xTaskRunState == taskTASK_NOT_RUNNING )
{
prvDeleteTCB( pxTCB );
}
/* Force a reschedule if the task that has just been deleted was running. */
if( ( xSchedulerRunning != pdFALSE ) && ( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) )
{
if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskYieldWithinAPI();
}
else
{
prvYieldCore( pxTCB->xTaskRunState );
}
}
taskEXIT_CRITICAL();
}
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */
traceRETURN_vTaskDelete();
}
#endif /* INCLUDE_vTaskDelete */
挂起
挂起函数的内容和删除函数大部分都是一样的。不再重复注释了。
区别就是:
1、删除任务会放到termination list里。挂起会放到suspended list。
2、挂起任务如果挂起的是当前任务,那么会判断如果调度器还没有启动,就需要改变pcurrenttcb的值,指向下个可以运行的任务。但是这是调度器没有启动,任务切换函数不能正常工作,那么就需要手动查找了。判断是不是所有的任务都被挂起了,实际不能有这种情况,因为至少有一个idle task。如果有任务没有被挂起,那么就vTaskSwitchContext()获取下一个任务。
#if ( INCLUDE_vTaskSuspend == 1 )
void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{
TCB_t * pxTCB;
#if ( configNUMBER_OF_CORES > 1 )
BaseType_t xTaskRunningOnCore;
#endif
traceENTER_vTaskSuspend( xTaskToSuspend );
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the running task that is
* being suspended. */
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
traceTASK_SUSPEND( pxTCB );
#if ( configNUMBER_OF_CORES > 1 )
xTaskRunningOnCore = pxTCB->xTaskRunState;
#endif
/* 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? */
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 = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
{
if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
{
/* The task was blocked to wait for a notification, but is
* now suspended, so no notification was received. */
pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
}
}
}
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
}
#if ( configNUMBER_OF_CORES == 1 )
{
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
{
/* 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 ) /*lint !e931 Right has no side effect, just volatile. */
{
/* 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();
}
}
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
if( xSchedulerRunning != pdFALSE )
{
/* Reset the next expected unblock time in case it referred to the
* task that is now in the Suspended state. */
prvResetNextTaskUnblockTime();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
{
if( xSchedulerRunning != pdFALSE )
{
if( xTaskRunningOnCore == ( BaseType_t ) portGET_CORE_ID() )
{
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
vTaskYieldWithinAPI();
}
else
{
prvYieldCore( xTaskRunningOnCore );
}
}
else
{
/* This code path is not possible because only Idle tasks are
* assigned a core before the scheduler is started ( i.e.
* taskTASK_IS_RUNNING is only true for idle tasks before
* the scheduler is started ) and idle tasks cannot be
* suspended. */
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL();
}
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */
traceRETURN_vTaskSuspend();
}
#endif /* INCLUDE_vTaskSuspend */
恢复
任务恢复有两个函数:vTaskResume()和xTaskResumeFromISR()。一个是用在任务中,一个是用在中断中。基本流程一样,以xTaskResume()说明:
#if ( INCLUDE_vTaskSuspend == 1 )
void vTaskResume( TaskHandle_t xTaskToResume )
{
TCB_t * const pxTCB = xTaskToResume;
traceENTER_vTaskResume( xTaskToResume );
/* It does not make sense to resume the calling task. */
configASSERT( xTaskToResume );
#if ( configNUMBER_OF_CORES == 1 )
/* The parameter cannot be NULL as it is impossible to resume the
* currently executing task. */
//调用恢复接口的任务肯定不能是当前正在运行的任务,因为这是矛盾的。否则就直接异常。
if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
#else
/* The parameter cannot be NULL as it is impossible to resume the
* currently executing task. It is also impossible to resume a task
* that is actively running on another core but it is not safe
* to check their run state here. Therefore, we get into a critical
* section and check if the task is actually suspended or not. */
if( pxTCB != NULL )
#endif
{
taskENTER_CRITICAL();
{
//判断是否是已经挂起的任务,只有挂起的任务才能恢复。
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
{
traceTASK_RESUME( pxTCB );
/* The ready list can be accessed even if the scheduler is
* suspended because this is inside a critical section. */
//从就绪列表移除
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
//恢复后就放到就绪列表。
prvAddTaskToReadyList( pxTCB );
/* This yield may not cause the task just resumed to run,
* but will leave the lists in the correct state for the
* next yield. */
taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceRETURN_vTaskResume();
}
#endif /* INCLUDE_vTaskSuspend */