FreeRTOS 第三章 任务

创建

对于用户而言,最常用的就是任务创建,接下来对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;
  1. pxTaskCode:task的执行函数。
  2. pcName:task的名字。
  3. usStackDepth:stack的深度,注意单位不是字节数,而是configTACK_DEPTH_TYPE,所以stack的大小是configTACK_DEPTH_TYPE*usStackDepth。
  4. pvParameters:传给task执行函数的参数。
  5. uxPriority:task优先级。越大优先级越高。
  6. 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 */

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依然@Fantasy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值