LMCH的个人空间 http://bbs.21ic.com/?1407155 [收藏] [复制] [分享] [RSS]

日志

FreeRTOS----任务及任务调度(一)

已有 3787 次阅读2017-6-3 16:03 |个人分类:RTOS|系统分类:嵌入式系统

3     任务及任务调度

3.1   任务概述

FreeRTOS中,应用程序由一组独立的任务组成。调度器负责决定哪个任务运行,它重复地轮流启动与停止各个任务。任务本身不知道调度器的活动情况,在任务自身看来,它是一直不停地在运行的(除非任务自己将自己的状态改变为阻塞态),任务无法知道自己何时会被别的任务中断执行,何时保存与恢复自己的运行状态,因此调度器在切换任务时要负责确保任务的上下文切换(各寄存器的保存与恢复)。为达到这个目的,每一个任务都要提供一个任务栈,调度器要切换任务时,将运行中的任务上下文保存在该任务的任务栈中,将来任务再次被切换回来的时候才能够正确地恢复其运行时的状态;同时即将要运行的任务则要将任务栈中上次保存的上下文恢复到处理器的寄存器中。

FreeRTOS是一个优先级抢占的操作系统,每一个任务都分配了一个优先级,优先级从0 ( configMAX_PRIORITIES - 1 ), configMAX_PRIORITIESFreeRTOSConfig.h文件中配置,其中0的优先级最低,(configMAX_PRIORITIES 1)的优先级最高。调度器总是先将处理器交给优先级最高的任务运行。

不同的任务可以处于同一个优先级,相同优先级的就绪任务可采用时间分片的方式共享处理器时间。

3.2   任务状态

FreeRTOS中的任务有以下几种状态,如图所示。


3.2.1         Running(运行状态)

当任务占用处理器执行的时候就说它处于运行状态。如果处理器只有单个核心,那么在任何时候都只有一个任务处于运行状态。

3.2.2         Ready(就绪状态)

就绪的任务是那些能够执行(处于非阻塞或非挂起状态),但是因为其它等于或高于该任务优先级的任务正在运行,当前没有运行的任务。就绪任务保存在就绪列表pxReadyTasksLists[ configMAX_PRIORITIES ]中,其位置与其优先级有关。

3.2.3         SuspendedReady(挂起就绪状态)

处于这种状态的任务是因为在调度器挂起时就绪,即应该就绪但是由于调度器被挂起又没能真正就绪的任务,他们被放在了xPendingReadyList列表中,调度器恢复时会访问该列表,将列表中的任务放入就绪列表。

3.2.4         Blocked(阻塞状态)

如果任务需要等待一段时间或者一个外部事件时,任务就处于阻塞状态。比如,一个任务调用了vTaskDelay()函数,它会阻塞直到延时期满(到期的事件)。任务也可以在等待队列、信号量、事件标志组、通知或者信号量事件时阻塞。阻塞状态的任务通常含有“超时”期限,该期限一过,任务应该解除阻塞状态,即使任务等待的事件没有发生。

处于阻塞状态的任务不占用处理器时间,并且不能进入运行状态。

3.2.5         Suspended(挂起状态)

和阻塞状态的任务类似,挂起状态的任务不能进入运行状态,但是挂起状态的任务没有超时。只有在调用vTaskSuspend()xTaskResume() API函数时,任务才进入或退出挂起状态。挂起的任务保存在挂起任务列表xSuspendedTaskList中。

不同的任务状态之间可以互相转换,方法是调用相关的API函数。

3.3   重要定义

3.3.1         结构体

3.3.1.1     任务控制块TCB

任务控制块TCBTask Control Block)结构体是实现任务调度与控制的重要数据类型。

typedef struct tskTaskControlBlock

{

/* 指向最后入栈的指针,它必须是TCB结构的第一个成员 */

volatile StackType_t   *pxTopOfStack; 

/*< MPU的设置在port层,它必须是TCB结构的第二个成员 */

#if ( portUSING_MPU_WRAPPERS/*0*/ == 1 )

xMPU_SETTINGS   xMPUSettings;      

#endif

ListItem_t       xStateListItem;    /* 任务状态链表Item,指示任务就绪、阻塞或等待 */

ListItem_t      xEventListItem;    /* 引用任务的事件链表Item */

UBaseType_t      uxPriority;         /* 任务优先级,0最低 */

StackType_t      *pxStack;           /* 任务栈指针 */

/* 创建任务时创建的描述任务的名称,便于系统调试 */

char   pcTaskName[ configMAX_TASK_NAME_LEN ];

#if ( portSTACK_GROWTH/*-1*/ > 0 )

/* 栈地址由低向高生长架构的任务栈尾指针 */

StackType_t    *pxEndOfStack;     

#endif

#if ( portCRITICAL_NESTING_IN_TCB/*0*/ == 1 )

/* 临界段的嵌套深度,机器无记录嵌套功能时使用 */

UBaseType_t    
uxCriticalNesting; 

#endif

#if ( configUSE_TRACE_FACILITY/*1*/ == 1 )

/* TCB创建次数,使调试确定任务删除或创建的时机 */

UBaseType_t     uxTCBNumber;       

UBaseType_t     uxTaskNumber;       /* 存储一个数字,共第三方调试代码使用 */

#endif

#if ( configUSE_MUTEXES/*1*/ == 1 )

/* 上一次为任务指定的优先级,用于优先级继承机制 */
UBaseType_t    uxBasePriority;  

UBaseType_t     uxMutexesHeld;

#endif

#if ( configUSE_APPLICATION_TASK_TAG == 1 )

TaskHookFunction_t pxTaskTag;/*
任务钩子函数 */
#endif

#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS/*0*/ > 0 )

void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];

#endif

#if( configGENERATE_RUN_TIME_STATS/*0*/ == 1 )

uint32_t       
ulRunTimeCounter;   /*
任务处于运行状态的总时间 */
#endif

#if ( configUSE_NEWLIB_REENTRANT/*0*/ == 1 )

struct  _reent xNewLib_reent;

#endif

#if( configUSE_TASK_NOTIFICATIONS/*1*/ == 1 )

volatile uint32_t ulNotifiedValue;

volatile uint8_t ucNotifyState;

#endif

#if( ( configSUPPORT_STATIC_ALLOCATION/*0*/ == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION/*1*/ == 1 ) )

uint8_t ucStaticallyAllocated; /* TCB
静态分配时设为pdTRUE */
#endif

#if( INCLUDE_xTaskAbortDelay/*0*/ == 1 )

uint8_t ucDelayAborted;

#endif

} tskTCB;

typedef tskTCB TCB_t;

3.3.1.2     任务状态结构体

typedef struct xTASK_STATUS

{

TaskHandle_t xHandle;           /* 任务句柄 */

const char *pcTaskName;         /* 指向任务名称的指针 */

UBaseType_t xTaskNumber;        /* 任务独一无二的一个号码 */

eTaskState eCurrentState;       /* 任务当前状态 */

UBaseType_t uxCurrentPriority;  /* 任务当前优先级 */

/* 为防止获得互斥量的任务优先级翻转,在优先级继承前,返回任务当前优先级  */

UBaseType_t uxBasePriority;

uint32_t ulRunTimeCounter;      /* 任务总的运行时间 */

StackType_t *pxStackBase;       /* 指向栈的最低地址 */

/* 任务创建以来,栈空间剩余量的最小值。值越小,越接近栈溢出 */

uint16_t usStackHighWaterMark;

} TaskStatus_t;

3.3.1.3     TimeOut结构体

TimeOut结构体在task.c文件中定义,但实际上用于消息队列中。

typedef struct xTIME_OUT

{

BaseType_t xOverflowCount;

TickType_t xTimeOnEntering;

} TimeOut_t;

3.3.2         任务列表

static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/* 就绪列表数组(按优先级分组) */

static List_t xDelayedTaskList1;       /* 延时列表1 */

static List_t xDelayedTaskList2;       /* 延时列表2 */

static List_t * volatile
pxDelayedTaskList;    /*
指向当前的延时列表指针 */

static List_t * volatile pxOverflowDelayedTaskList; /* 指向计数溢出延时列表的指针 */

static List_t xPendingReadyList; /* 任务调度器被挂起时,就绪的任务列表。当调度器恢复时,该列表的任务应被转移到就绪列表 */

xDelayedTaskList1xDelayedTaskList2用于任务的延时控制,其作用将在3.4.4小节中讲到。

pxDelayedTaskListpxOverflowDelayedTaskList各指向上面的某个列表。

3.3.3         任务相关的变量

static volatile UBaseType_t uxCurrentNumberOfTasks  = ( UBaseType_t )0U;

static volatile TickType_t xTickCount               = ( TickType_t ) 0U;

static volatile UBaseType_t uxTopReadyPriority      = tskIDLE_PRIORITY;

static volatile BaseType_t xSchedulerRunning        = pdFALSE;

static volatile UBaseType_t uxPendedTicks           = ( UBaseType_t ) 0U;

static volatile BaseType_t xYieldPending            = pdFALSE;

static volatile TickType_t xNextTaskUnblockTime     = ( TickType_t )0U;

static TaskHandle_t xIdleTaskHandle                 = NULL;

static volatile UBaseType_t uxSchedulerSuspended    = ( UBaseType_t )pdFALSE;

uxCurrentNumberOfTasks记录了系统内核中当前任务数,系统中至少有一个空闲任务。

xTickCount为系统时基计数。

uxTopReadyPriority记录了系统就绪任务的优先级或最高优先级。如果configUSE_PORT_OPTIMISED_TASK_SELECTION配置为0uxTopReadyPriority记录系统最高优先级;如果配置为1uxTopReadyPriority的某位为1,代表有该位表示的优先级任务就绪,例如uxTopReadyPriority=101101(二进制),则表示有0235优先级任务的任务已就绪等待执行。

xSchedulerRunning为调度器运行状态标志,为pdTRUE表示调度器正在运行,为pdFALSE则表示调度器没有运行。在系统中启动调度器后应避免调度器停止运行。

uxSchedulerSuspended为调度器挂起状态标志。调度器不希望被停止,但是调度器允许挂起,为pdTRUE表示调度器被挂起,挂起时不会进行任务的切换;为pdFALSE时表示调度器未被挂起,需要任务切换时会进行任务的切换。

uxPendedTicks为调度器挂起计时。在调度器挂起时,该参数记录挂起的时间,调度器恢复时,该值复位为0

xYieldPending为任务切换标志,为pdTRUE表示系统有任务切换的请求被挂起,在调度器被挂起后恢复时,将判断xYieldPending的值,便进行任务切换,在内核响应请求进行任务切换时被复位为pdFALSE

xNextTaskUnblockTime下一个任务解除阻塞状态的时间。

xIdleTaskHandle为空闲任务句柄。

3.3.4         宏定义

3.3.4.1     taskRECORD_READY_PRIORITY( uxPriority )

该宏记录在将任务插入就绪列表时调用,目的是记录内核中就绪任务的最高优先级(configUSE_PORT_OPTIMISED_TASK_SELECTION == 0时),或记录有哪些优先级的任务已就绪(configUSE_PORT_OPTIMISED_TASK_SELECTION == 1时)。

#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )

#define taskRECORD_READY_PRIORITY( uxPriority )         \

{                                                                                                  \

if( ( uxPriority ) > uxTopReadyPriority )                               \

{                                                                                                 \

uxTopReadyPriority = ( uxPriority );                                  \

}                                                                                              \

}

#else

#define taskRECORD_READY_PRIORITY( uxPriority )            \

portRECORD_READY_PRIORITY( uxPriority,uxTopReadyPriority )

#endif

3.3.4.2     taskRESET_READY_PRIORITY( uxPriority )

该宏复位某一优先级在uxTopReadyPriority中的标记,在该优先级任务的就绪列表中没有任务时调用。

#if (configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )

#define taskRESET_READY_PRIORITY( uxPriority )

#else

#define taskRESET_READY_PRIORITY( uxPriority )                      \

{                                                                                                             \
if( listCURRENT_LIST_LENGTH( 
&( pxReadyTasksLists[ (uxPriority ) ] ) ) == ( UBaseType_t ) 0 )         \
{                                                                                                          \

portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \

}                                                     \

}

3.3.4.3     taskSELECT_HIGHEST_PRIORITY_TASK()

该宏在需要任务切换的时候调用,用于选择就绪列表中最高优先级任务。

#if (configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )   
#define taskSELECT_HIGHEST_PRIORITY_TASK()                                   \

{                                                                                                                      \
UBaseType_t uxTopPriority = uxTopReadyPriority;                            \

while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ))              \

{                                                                                                                         \

configASSERT( uxTopPriority );                                      \           
--uxTopPriority;                                                  \
      
 }                                                                   \
       
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,
&( pxReadyTasksLists[ uxTopPriority ] ));             \       
uxTopReadyPriority = uxTopPriority;                                       \

    }

#else    
#define taskSELECT_HIGHEST_PRIORITY_TASK()                                 \

{                                                                         \     
  UBaseType_t uxTopPriority;                                             \
                                                                                 
/*
给出最高优先级的就绪列表中的任务  */                               \       
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );             \

configASSERT( listCURRENT_LIST_LENGTH(  &( pxReadyTasksLists[ uxTopPriority ] )) > 0 );                 \        
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,   
&( pxReadyTasksLists[ uxTopPriority ] ));                        \

}

#endif

3.3.4.4     prvAddTaskToReadyList(pxTCB )

该宏将任务插入就绪列表。它首先调用taskRECORD_READY_PRIORITY()宏,记录任务的优先级,然后将任务插入就续列表中。

#define prvAddTaskToReadyList( pxTCB )                                       \   
traceMOVED_TASK_TO_READY_STATE( pxTCB );/* nothing */                  \
   
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                       \

vListInsertEnd( &( pxReadyTasksLists[ (pxTCB )->uxPriority ] ),    &( ( pxTCB )->xStateListItem ));                                             \  
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )/* nothing */

3.3.4.5     taskSWITCH_DELAYED_LISTS()

用于延时列表的切换,在系统任务调度前,将判断延时列表中的任务是否到期,如果有任务到期,必须将任务TCB转移到就绪列表中。而任务延时到期的计时是以系统时基计数值来计算的,随着系统连续运行时间越来越长,时基计数值(比如32位)会溢出,这时需要切换列表。系统使用了两个延时列表来存放需要延时执行的任务,分别是5.3介绍的xDelayedTaskList1xDelayedTaskList2,用两个指针pxDelayedTaskListpxOverflowDelayedTaskList指向两个列表。

举个例子:如果系统时基计数现在到了0x ffff fff0,而某个任务Task1从这个时刻起需要延时20个时基值,那么它下次唤醒的时基计数为0x 1 0000 0004,但是这个计算结果已经溢出了,实际得到的是0x 0000 0004,则将该任务放入pxOverflowDelayedTaskList指向的列表,pxDelayedTaskList指向的是当前系统正在使用的延时任务列表。系统运行到系统时基计数0x ffff ffff后,下一个时基节拍到来时,该计数值下一个计算结果变成了0x 0000 0000,这时便进行延时列表切换,将pxOverflowDelayedTaskListpxDelayedTaskList指向的列表对换,这时任务Task1便可以被正常唤醒了。

#define taskSWITCH_DELAYED_LISTS()                                    \

{                                                              \   
List_t *pxTemp;                                                  \
   
/*
延时列表在切换时应该是空的,因为在溢出前(0xffff ffff时刻) 将会处理完表中所有的延时任务  */                           \  
configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );              \
   
pxTemp = pxDelayedTaskList;                                      \
   
pxDelayedTaskList = pxOverflowDelayedTaskList;                      \
   
pxOverflowDelayedTaskList = pxTemp;                              \

xNumOfOverflows++;          \
prvResetNextTaskUnblockTime();                                  \

}

3.3.4.6     prvAddTaskToReadyList( pxTCB )

将任务插入到就绪列表。

#define prvAddTaskToReadyList( pxTCB )                                    \ 
traceMOVED_TASK_TO_READY_STATE( pxTCB );/* nothing */                \

taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                     \
 
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ),
&(( pxTCB )->xStateListItem ) );    \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )/* nothing */

3.4   任务及调度API

3.4.1         xTaskAbortDelay(TaskHandle_t xTask

任务退出等待状态。函数首先调用vTaskSuspendAll()(3.4.41)挂起调度器,即在执行该函数时,将停止对任务的调度,直到函数完成所有操作才调用xTaskResumeAll()3.4.42节)重启调度。然后调用eTaskGetState()3.4.20节)检查任务是否处于等待状态,是的话便将任务从等待列表中删除,并插入到就绪列表中,根据任务优先级情况选择更新xYieldPending

                

xTask           任务句柄

         pdFALSE
BaseType_t xTaskAbortDelay( TaskHandle_t xTask )

{ 
TCB_t *pxTCB = ( TCB_t * ) xTask;

BaseType_t xReturn = pdFALSE;

configASSERT( pxTCB );

vTaskSuspendAll();//
挂起调度器
{
 
/*
只能终止确实处于阻塞状态的任务,所以先获取任务状态 */
   if( eTaskGetState( xTask ) == eBlocked )   
  {   

               /* 将任务从状态列表中删除(阻塞态) */

                ( void ) uxListRemove( &(pxTCB->xStateListItem ) );

                /* 如果任务阻塞在等待某一事件,也要将它从事件列表中删除 */

                taskENTER_CRITICAL();//进入临界段

          {

                    if(listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )

                    {

                        ( void ) uxListRemove(&( pxTCB->xEventListItem ) );            

                        pxTCB->ucDelayAborted = pdTRUE;

                    }

                    else

                    {                       
                       mtCOVERAGE_TEST_MARKER();//nothing

                    }

                }

                taskEXIT_CRITICAL();//退出临界段

                /* 将任务插入到就绪列表 */

                prvAddTaskToReadyList( pxTCB );

                /* 如果终止等待的任务优先级比当前任务优先级高,要提交任务切换 */

                #if (  configUSE_PREEMPTION/*1*/ == 1 )

                {

                    if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )

                    {

                        xYieldPending = pdTRUE;

                    }

                    else

                    {                       
                        mtCOVERAGE_TEST_MARKER();//nothing

                    }

                }

                #endif /* configUSE_PREEMPTION */           
}
           
else
          
{
              
mtCOVERAGE_TEST_MARKER();//nothing
           
}
      
}
       
xTaskResumeAll();//
恢复调度器       
return xReturn;

}

这里有个临界段的概念。临界段是进程、任务或中断访问共享的资源时,为了防止在访问的过程中被其它进程、任务或中断打断其访问并修改资源内容,导致系统崩溃而采取的一种措施,访问前便采取措施禁止其它进程、任务或中断对资源的访问,或直接禁止其它进程、任务或中断的运行,访问结束后再解除禁令,在这中间执行的代码便是临界段的代码。在这里是调用taskENTER_CRITICAL()进入临界段,调用taskEXIT_CRITICAL()退出临界段。

3.4.2         xTaskCallApplicationTaskHook(TaskHandle_t xTask, void *pvParameter )

调用任务钩子函数。任务TCB中有一个指针类型的字段是pxTaskTag,用于设置任务的钩子函数。如果开发人员实现了钩子函数,在应用中可以调用它。

                

xTask        任务句柄

pvParameter   传递的参数指针

         任务钩子函数的执行结果
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter )

  {   
TCB_t *xTCB;
   
BaseType_t xReturn;
       
if( xTask == NULL )

{          
xTCB = ( TCB_t * ) pxCurrentTCB;
       
}
       
else
      
{
          
xTCB = ( TCB_t * ) xTask;
      
}
      
if( xTCB->pxTaskTag != NULL )
       
{
           
xReturn = xTCB->pxTaskTag( pvParameter );
      
}
       
else
      
{
          
xReturn = pdFAIL;
      
}
      
return xReturn;

 }

任务钩子函数的原型如下。

BaseType_t ApplicationTaskHook( void*pvParameter )

3.4.3         xTaskCreate(TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask )

创建任务,调用pvPortMalloc()1.4.1)在内核内存中为任务分配TCB和任务栈空间,然后调用prvInitialiseNewTask()prvAddNewTaskToReadyList()初始化任务并将任务插入就绪列表。

                

pxTaskCode      任务函数指针

pcName   任务名称指针

usStackDepth  任务栈深度

pvParameters 传递给任务的参数

uxPriority 任务优先级

pxCreatedTask         用于输出的任务句柄

         如果任务创建成功,返回pdPASS
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters,

 UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask )

    {
TCB_t *pxNewTCB;       

BaseType_t xReturn;

/*
如果栈向下生长,先分配栈,再分配TCB,保证栈不会超过TCB;如果栈向上生长,  那么先分配TCB再分配栈。 */
#if( portSTACK_GROWTH/* -1 */ > 0 )//
如果向上生长
{

/*
分配TCB空间。 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );//
为任务TCB分配内存
if( pxNewTCB != NULL )

{

                /* 分配任务栈空间,栈的基址存储在TCB中。任务的栈也是在内核的内存中分配 */

                pxNewTCB->pxStack = (StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof(StackType_t ) ) );

                if( pxNewTCB->pxStack ==NULL )

                {

                    /* 如果分配任务栈失败,删除分配的TCB */

                    vPortFree( pxNewTCB );

                    pxNewTCB = NULL;

                }
}

}

#else /* portSTACK_GROWTH *///
向下生长
{

StackType_t *pxStack;

 /*
为任务分配栈空间,深度*4(字节) */
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth )* sizeof( StackType_t ) ) );

if( pxStack != NULL )
    
{

                /* 为任务分配TCB空间 */

                pxNewTCB = ( TCB_t * )pvPortMalloc( sizeof( TCB_t ) );

                if( pxNewTCB != NULL )

                {

                    /* 将栈的地址保存在TCB */

                    pxNewTCB->pxStack =pxStack;

                }

                else

                {

                    /* TCB分配失败,释放分配的栈空间*/

                    vPortFree( pxStack );

                }
}

else

{

                pxNewTCB = NULL;
}

}

#endif /* portSTACK_GROWTH */

if( pxNewTCB != NULL )//
如果TCB和栈分配成功
{

#if( configSUPPORT_STATIC_ALLOCATION == 1 )//0

{

                /* 任务可以静态或动态分配内存,因此注意任务是动态分配的,以便万一需要删除它 */
              pxNewTCB->ucStaticallyAllocated = pdFALSE;

 }         
#endif /* configSUPPORT_STATIC_ALLOCATION */

/*
初始化新任务 */
prvInitialiseNewTask( pxTaskCode, pcName, usStackDepth, pvParameters,uxPriority, pxCreatedTask, pxNewTCB );

/*
将任务添加到就绪列表 */
prvAddNewTaskToReadyList( pxNewTCB );

xReturn = pdPASS;//pdTRUE
1
}

else

{

xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;//-1

return xReturn;//
返回值,成功则1,失败则-1

    }

3.4.3.1     prvInitialiseNewTask(TaskFunction_t pxTaskCode, const char * const pcName, const uint16_tusStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t  *const pxCreatedTask, TCB_t *pxNewTCB )

初始化新创建的任务,它初始化任务TCB的各项参数,包括栈顶地址、优先级、任务名称等,然后调用函数pxPortInitialiseStack()对任务栈初始化,以便任务能够顺利启动。

                

pxTaskCode      任务函数指针

pcName   任务名称指针

usStackDepth  任务栈深度

pvParameters 传递给任务的参数

uxPriority 任务优先级

pxCreatedTask         用于输出的任务句柄

pxNewTCB       任务TCB指针

        

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth,

void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask,TCB_t *pxNewTCB )

{

StackType_t *pxTopOfStack;

UBaseType_t x;
#if( portUSING_MPU_WRAPPERS/*0*/== 1 )

/*
任务创建为特权级模式 */
BaseType_t xRunPrivileged;

if( ( uxPriority/*
任务优先级 */ & portPRIVILEGE_BIT/* 0 */ ) != 0U )
{

xRunPrivileged = pdTRUE;

}

else

{

xRunPrivileged = pdFALSE;

}

uxPriority &= ~portPRIVILEGE_BIT;

#endif /* portUSING_MPU_WRAPPERS == 1 */

#if    ( (configCHECK_FOR_STACK_OVERFLOW > 1/* 0 */ ) || 
  ( configUSE_TRACE_FACILITY == 1/* 1 */ ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 /* 0 */) )

    {
/*
将栈填上0xa5U,在后面可以依据地址的值是否为0xa5U判断任务运行过程中是否用到了该位置的栈,调试时便可监视为任务分配的栈是否够用 */
( void ) memset( pxNewTCB->pxStack, ( int )tskSTACK_FILL_BYTE/*0xa5U*/, ( size_t ) usStackDepth * sizeof( StackType_t ) );

    }
#endif

/*
计算栈顶的地址。这取决于栈的生长方向 */
#if( portSTACK_GROWTH < 0 )//-1
,向下生长

    { 
pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( uint16_t ) 1 );

/*
地址向下按4字节对齐 */
pxTopOfStack = ( StackType_t * )
( ( (   portPOINTER_SIZE_TYPE )pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/*
检查栈顶地址对齐的正确性 */

configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

}

#else /* portSTACK_GROWTH */  //向上生长

  {

pxTopOfStack = pxNewTCB->pxStack;
/*
检查栈地址是否对齐 */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack &
( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
/*
栈空间极大值 */
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack +
( usStackDepth - ( uint16_t )     1 );

    }
#endif /* portSTACK_GROWTH */

/*
保存任务名称 */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t )configMAX_TASK_NAME_LEN; x++ )

    {
pxNewTCB->pcTaskName[ x ] = pcName[ x ];

/*
如果任务名称的字符串长度比configMAX_TASK_NAME_LEN小,不必全部填满 */
if( pcName[ x ] == 0x00 )

{

break;

}

else

{
mtCOVERAGE_TEST_MARKER();

}

   }
/*
确保任务名称字符串不超过最大长度。 */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

/*
优先级作为一个数组的索引值,需确保它不会大于最高优先级。首先移除特权位 */
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )

    {
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;

    }
else

    {
mtCOVERAGE_TEST_MARKER();

    }
pxNewTCB->uxPriority = uxPriority;

#if ( configUSE_MUTEXES == 1 )//1
,使用互斥信号量

    {
pxNewTCB->uxBasePriority = uxPriority;

pxNewTCB->uxMutexesHeld = 0;

    }
#endif /* configUSE_MUTEXES */

/*
初始化TCB的状态和事件列表项 */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );

vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

/*
设置状态列表项的Owner为任务自身。 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB
);

/*
设置事件列表项的值为:最大优先级-任务优先级,Owner为任务自身

    listSET_LIST_ITEM_VALUE(&( pxNewTCB->xEventListItem ), ( TickType_t )configMAX_PRIORITIES - ( TickType_t ) uxPriority );
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB
);

#if ( portCRITICAL_NESTING_IN_TCB == 1 )//0

    {
pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;

    }
#endif /* portCRITICAL_NESTING_IN_TCB */

#if ( configUSE_APPLICATION_TASK_TAG == 1 )//0

    {
pxNewTCB->pxTaskTag = NULL;

    }
#endif /* configUSE_APPLICATION_TASK_TAG */

#if ( configGENERATE_RUN_TIME_STATS == 1 )//0

    {
pxNewTCB->ulRunTimeCounter = 0UL;

    }
#endif /* configGENERATE_RUN_TIME_STATS */

#if ( portUSING_MPU_WRAPPERS == 1 )//0

    {
vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions,pxNewTCB->pxStack, usStackDepth );

    }
#endif

#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )//0

    {
for( x = 0; x < ( UBaseType_t )configNUM_THREAD_LOCAL_STORAGE_POINTERS/* 0 */; x++ )

{

pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;

}

    }
#endif

#if ( configUSE_TASK_NOTIFICATIONS == 1 )//1

    {
pxNewTCB->ulNotifiedValue = 0;

pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;//0

    }
#endif

    #if( configUSE_NEWLIB_REENTRANT == 1 )//0

    {
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );

    }
#endif

#if( INCLUDE_xTaskAbortDelay == 1 )//0

    {
pxNewTCB->ucDelayAborted = pdFALSE;

    }
#endif

/* 初始化TCB栈。返回的地址设为任务的起始地址。任务栈一旦初始化, 栈顶的值也同时更新。 */
#if( portUSING_MPU_WRAPPERS == 1 )//0

    {
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,pxTaskCode, pvParameters, xRunPrivileged );

    }
#else /* portUSING_MPU_WRAPPERS */

    {
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,pxTaskCode, pvParameters );

    }
#endif /* portUSING_MPU_WRAPPERS */

if( ( void * ) pxCreatedTask != NULL )

    {
/*
将任务句柄传递给pxCreatedTask。这个句柄可以用于改变任务优先级、删除任务等 */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;

    }
else

    { 
mtCOVERAGE_TEST_MARKER();

    }

}

3.4.3.2     *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )

任务栈初始化函数,函数实现与采用的处理器有关,函数位于port.c文件中。它主要完成刚创建的任务栈的初始化。笔者使用的是STM32F429芯片,它是基于ARM Cortex-M4的处理器,栈向下生长。

                

pxTopOfStack   任务栈顶的指针

pxCode     任务函数

pvParameters 传递给任务的参数

         栈顶的值

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )

{
pxTopOfStack--;
 
*pxTopOfStack = portINITIAL_XPSR;  
/* xPSR */

pxTopOfStack--;

*pxTopOfStack = ( StackType_t ) pxCode; /* PC */
   
pxTopOfStack--;

*pxTopOfStack = ( StackType_t ) prvTaskExitError;   /* LR */

pxTopOfStack -= 5;

*pxTopOfStack = ( StackType_t ) pvParameters;   /* R0 */

pxTopOfStack--;

*pxTopOfStack = portINITIAL_EXEC_RETURN;//0xfffffffd

pxTopOfStack -= 8;

return pxTopOfStack;

}

3.4.3.3     prvAddNewTaskToReadyList(TCB_t *pxNewTCB )

将新创建的任务插入到就绪列表。这个函数做的事情比较多,他要根据系统的任务数量、任务优先级、调度是否已经启动的情况去决定下一步的动作。

如果创建的任务是系统第一个任务,要初始化系统的各个任务列表;如果调度器还没有启动,则每次都将当前任务设置为创建的任务中优先级最高的任务;如果调度器已经启动,又如果新创建的任务优先级更高,需要提交切勿切换请求,在ARM中是提交PendSV中断,由中断服务程序实现任务上下文的切换。

                

pxNewTCB       新任务的TCB指针

        

static void prvAddNewTaskToReadyList( TCB_t*pxNewTCB )

{

/* 就绪列表更新时确保中断不会访问任务列表 */
taskENTER_CRITICAL();//
进入临界段

    {    
/*
任务数加1 */
uxCurrentNumberOfTasks++;

if( pxCurrentTCB == NULL )

{

/*
没有其它任务,或者其它任务都处于挂起的状态,使该任务为当前任务 */
pxCurrentTCB = pxNewTCB;

if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )

{

                /* 任务数是1,表明这是第一个被创建的任务,所以要求对就绪列表初始化。如果就绪列表初始化函数调用失败,将报告失败 */

                prvInitialiseTaskLists();       
}

else

{

                mtCOVERAGE_TEST_MARKER();
}

}
    
else
    
{
     
/*
创建的任务不是唯一的任务,但调度器还没有运行,且创建的任务是目前为止的最高优先级任务,则设为当前任务 */
if( xSchedulerRunning == pdFALSE )

{

                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )

                {

                    pxCurrentTCB = pxNewTCB;

                }

                else

                {
mtCOVERAGE_TEST_MARKER();//nothing

                }
}

else

{

mtCOVERAGE_TEST_MARKER();//nothing

      }

}

uxTaskNumber++;//
任务编号数加1
#if ( configUSE_TRACE_FACILITY == 1 )//1

{

/*
为了追踪任务,增加的TCB计数器,指示了任务是第几个创建的任务 */
pxNewTCB->uxTCBNumber = uxTaskNumber;

}

#endif /* configUSE_TRACE_FACILITY */

traceTASK_CREATE( pxNewTCB );//nothing

/*
将任务插入到就绪列表 */
prvAddTaskToReadyList( pxNewTCB );

portSETUP_TCB( pxNewTCB );//nothing

    }
taskEXIT_CRITICAL();//
退出临界段
if( xSchedulerRunning != pdFALSE )//
如果调度器正在运行

    {
/*
如果创建的任务优先级比当前的任务优先级高,它应该立即执行 */
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )

{

/* portYIELD_WITHIN_API()
portYIELD(),触发PendSV */
taskYIELD_IF_USING_PREEMPTION();

}

else

{
   
mtCOVERAGE_TEST_MARKER();//nothing

}

    }
else

    {
mtCOVERAGE_TEST_MARKER();//nothing

    }

}

3.4.3.4     prvInitialiseTaskLists(void )

任务列表初始化函数。在创建第一个系统任务时会调用该函数,用于初始化系统的各个任务列表。

                

    

        

static void prvInitialiseTaskLists( void )

{

UBaseType_t uxPriority;
/*
0优先级开始,到最高优先级,对就绪列表逐个进行初始化 */
for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t )configMAX_PRIORITIES; uxPriority++ )

    {
vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );

    }
/*
xDelayedTaskList1xDelayedTaskList2xPendingReadyList列表初始化 */
vListInitialise( &xDelayedTaskList1 );

vListInitialise( &xDelayedTaskList2 );

vListInitialise( &xPendingReadyList );

#if ( INCLUDE_vTaskDelete == 1 )//1

    {
/*
初始化xTasksWaitingTermination列表 */
vListInitialise( &xTasksWaitingTermination );

    }
#endif /* INCLUDE_vTaskDelete */

#if ( INCLUDE_vTaskSuspend == 1 )//1

    {

        /* 初始化xSuspendedTaskList列表 */
vListInitialise( &xSuspendedTaskList );

    }
#endif /* INCLUDE_vTaskSuspend */

/*
指定pxDelayedTaskListpxOverflowDelayedTaskList */
pxDelayedTaskList = &xDelayedTaskList1;

pxOverflowDelayedTaskList = &xDelayedTaskList2;

}

3.4.3.5     taskYIELD_IF_USING_PREEMPTION()

任务切换请求,这是一个宏定义的函数,在ARM中是提交PendSV中断。

                

    

        

#if( configUSE_PREEMPTION == 0 )
#define taskYIELD_IF_USING_PREEMPTION()

#else
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()

#endif
3.4.4         vTaskDelay(const TickType_t xTicksToDelay )

将调用该函数的任务设置为阻塞状态,阻塞时间由xTicksToDelay给出。函数首先计算任务到期的时间,并将任务从就绪列表转移到延时列表。在任务调度时,内核将检查延时列表中的任务到期的时间同当前时间对比,如果到期便又将会被其转移到就绪列表,并根据其优先级确定是否需要进行任务切换。

                

xTicksToDelay  延时的时基数

        
void vTaskDelay( const TickType_t xTicksToDelay )

    {
BaseType_t xAlreadyYielded = pdFALSE;//
记录是否触发了任务调度
/* 0
延时时进行调度 */
if( xTicksToDelay > ( TickType_t ) 0U )//
没到延时时间
 {

configASSERT( uxSchedulerSuspended == 0 );

vTaskSuspendAll();//
挂起所有任务
{

                traceTASK_DELAY();//nothing

                /* 调度器被挂起时,在调度器恢复之前,从事件列表中删除的任务,不会被放入就绪列表或从阻塞列表中删除。*/     
      prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE );
    
}

//
恢复所有任务,如果在恢复任务过程中出现了任务切换,返回pdTRUE
xAlreadyYielded = xTaskResumeAll();

}

else

{
   
mtCOVERAGE_TEST_MARKER();//nothing

}

/*
如果xTaskResumeAll()函数没有触发任务调度,则在这里触发任务调度 */
if( xAlreadyYielded == pdFALSE )

{

portYIELD_WITHIN_API();//
提交PendSV异常以切换任务
}

else

{

mtCOVERAGE_TEST_MARKER();//nothing

}

    }

3.4.4.1     prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )

将当前任务添加到延时等待列表。它首先将任务从就绪列表中移出,如果就绪列表为空,要更新uxTopReadyPriority;然后根据参数xTicksToWait和当前的系统时基计算任务唤醒的时间,再根据系统配置将任务插入到延时列表或者挂起列表,最后再更新xNextTaskUnblockTime的值,系统调度时要依据该值xNextTaskUnblockTime去判断是否需要唤醒某个任务。

                

xTicksToDelay  延时的时基数

xCanBlockIndefinitely      是否需要无限期阻塞任务

        

static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )

{

TickType_t xTimeToWake;

const TickType_t xConstTickCount =xTickCount;
#if( INCLUDE_xTaskAbortDelay/*0*/ == 1 )

    {
/*
将要进入延时列表,ucDelayAborted标志复位为pdFALSE,当函数退出等待时会设置为pdTRUE */
pxCurrentTCB->ucDelayAborted = pdFALSE;

    }
#endif

/*
将任务插入阻塞列表前,从就绪列表中删除。 */
/*
如果任务所在的就绪列表中没有了任务,则需要设置uxTopReadyPriority */
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == (UBaseType_t ) 0 )

    {
/*
复位当前任务在uxTopReadyPriority的优先级标记 */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority,
uxTopReadyPriority );

    }
else

    {
mtCOVERAGE_TEST_MARKER();//nothing

    }
#if ( INCLUDE_vTaskSuspend/*1*/ == 1 )//
如果使能了任务挂起

    {
/*
如果需要阻塞任务的时间为最大值,且允许无限期阻塞任务 */
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely!= pdFALSE ) )

{

/*
将任务放入挂起列表,而不是放入延时列表,确保任务不会被计时事件唤醒,它将被无限期阻塞 */
vListInsertEnd( &xSuspendedTaskList, &(pxCurrentTCB->xStateListItem ) );

}

else//xTicksToWait
= portMAX_DELAY ) && ( xCanBlockIndefinitely == pdFALSE )
{

/*
以下情况为,任务没有收到事件时,将阻塞给定的时间计算如果事件没发生,任务应该被唤醒的时间。该值可能溢出,但是没有关系,
*
内核能够正确处理 */
xTimeToWake = xConstTickCount + xTicksToWait;

/*
列表按照唤醒时间进行排序 */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ),xTimeToWake );

if( xTimeToWake < xConstTickCount )//
如果计算值溢出了
{

        /*
插入到列表pxOverflowDelayedTaskList */

                vListInsert(pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}

else//
没有溢出
{

                /* 插入到列表pxDelayedTaskList*/

                vListInsert( pxDelayedTaskList,&( pxCurrentTCB->xStateListItem ) );

                /* 如果新的到期时间小于内核保存的下一个到期时间,则更新它 */

                if( xTimeToWake < NextTaskUnblockTime )

                {

                    xNextTaskUnblockTime =xTimeToWake;
   }

                else

                {
mtCOVERAGE_TEST_MARKER();//nothing

                }
}

}

    }
#else /* INCLUDE_vTaskSuspend
,如果不支持任务挂起 */

    {
/*
计算如果事件没发生,任务应该被唤醒的时间。该值可能溢出,但是没有关系,内核能够正确处理 */
xTimeToWake = xConstTickCount + xTicksToWait;

/*
列表按照唤醒时间进行排序 */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ),xTimeToWake );

if( xTimeToWake < xConstTickCount )

{

     /*
插入到列表pxOverflowDelayedTaskList */
vListInsert( pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem ) );

}

else

{
   
/*
插入到当前列表pxDelayedTaskList */
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem )
);

/*
如果新的到期时间小于内核保存的下一个到期时间,则更新它 */
if( xTimeToWake < xNextTaskUnblockTime )

{

                xNextTaskUnblockTime = xTimeToWake;
}

else

{

                mtCOVERAGE_TEST_MARKER();//nothing
}

}

( void ) xCanBlockIndefinitely;

    }
#endif /* INCLUDE_vTaskSuspend */

}

3.4.5  vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

类似于vTaskDelay(),使任务阻塞等待一段时间,只是它是根据参数pxPreviousWakeTimexTimeIncrement计算下次唤醒任务的时间,根据计算结果和当前系统时基判断是否阻塞任务,最后还要更新pxPreviousWakeTime指向的内存的值,以便下次再次计算。

不同于vTaskDelay()的是,vTaskDelay()调用后,任务的唤醒时间依据调用该函数系统时基而定,而vTaskDelayUntil()依据任务上次唤醒的时间而定,它更适合于需要固定频率地执行的任务。

                

pxPreviousWakeTime      任务上次唤醒的时刻

xTimeIncrement      任务阻塞等待的时间

        
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

    {
TickType_t xTimeToWake;

BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

configASSERT( pxPreviousWakeTime );

configASSERT( ( xTimeIncrement > 0U ) );

configASSERT( uxSchedulerSuspended == 0 );

vTaskSuspendAll();//
挂起调度器
{

/*
时基计数在这一段程序中不能改变 */
const TickType_t xConstTickCount = xTickCount;

/*
生成任务唤醒的时基时间 */
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;

/*
如果系统时间小于传入的时间起点,说明系统时基溢出 */
if( xConstTickCount < *pxPreviousWakeTime )

{

                /* 在调用该函数时,系统时基计数已经溢出了。这种情况下,只有任务唤醒的时间也在计算时溢出了,才能用之做延时. */

                if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )

                {

                    xShouldDelay = pdTRUE;//延时计算有效,仍然延时

                }

                else

                {
mtCOVERAGE_TEST_MARKER();//nothing

                }
}

else

{

                /* 时基计数没有溢出。这种情况下,不管计算的唤醒时间是否溢出,都对任务延时 */

                if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )

                {

                    xShouldDelay = pdTRUE;

                }

                else
          {

mtCOVERAGE_TEST_MARKER();//nothing

                }
}

/*
更新唤醒时间,以便下次调用 */
*pxPreviousWakeTime = xTimeToWake;

if( xShouldDelay != pdFALSE )//
如果需要延时
{

                traceTASK_DELAY_UNTIL( xTimeToWake);//nothing

                /* prvAddCurrentTaskToDelayedList()需要阻塞时间,不是唤醒的时间,所以要减去当前时基计数 */

                prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
}
     
else
   
{

mtCOVERAGE_TEST_MARKER();//nothing

}

}

xAlreadyYielded = xTaskResumeAll();//
恢复调度器
/*
如果xTaskResumeAll()中没有触发任务调度,则触发任务调度。*/
if( xAlreadyYielded == pdFALSE )

{

portYIELD_WITHIN_API();//
提交PendSV异常以切换任务
}

else

{

mtCOVERAGE_TEST_MARKER();//nothing

}

    }

3.4.6         vTaskDelete( TaskHandle_t xTaskToDelete )

删除任务。函数首先将任务从所处状态列表和事件列表中移出,如果待删除的任务不是当前任务,直接调用prvDeleteTCB()将任务删除,回收任务TCB和栈占用的内存,因为删除的任务可能处于阻塞等待的状态,因此要更新一次xNextTaskUnblockTime;如果删除的任务是当前正在执行的任务(即任务删除自己本身),则将任务TCB放入xTasksWaitingTermination列表,待任务切换至空闲任务后由空闲任务删除之,且要提交一次PendSV使系统完成任务切换。

                

xTaskToDelete 待删除任务的句柄

        
void vTaskDelete( TaskHandle_t xTaskToDelete )

    {
TCB_t *pxTCB;

taskENTER_CRITICAL();//
进入临界段
{

/*
获取TCB指针 */
pxTCB = prvGetTCBFromHandle( xTaskToDelete );

/*
将任务从状态列表中删除,并更新uxTopReadyPriority的值 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t )0 )

{

                taskRESET_READY_PRIORITY(pxTCB->uxPriority );
}

else

{

mtCOVERAGE_TEST_MARKER();//nothing

            }
/*
将任务从事件列表中删除 */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL)
   
{

                ( void ) uxListRemove( &(pxTCB->xEventListItem ) );
}
     
else

{

                mtCOVERAGE_TEST_MARKER();//nothing
}

if( pxTCB == pxCurrentTCB )

{

                /* 如果删除的是当前运行的任务,任务自身无法完成操作,因为要求切换到下一个任务。将任务放入终止列表。空闲任务将检查终止列

                 * 表,并释放内存。 */
   vListInsertEnd(&xTasksWaitingTermination,
  &(pxTCB->xStateListItem ) );

                /* uxDeletedTasksWaitingCleanUp1,空闲任务依此判断xTasksWaitingTermination中有任务等待删除 */

                ++uxDeletedTasksWaitingCleanUp;
}

else//
删除的不是当前运行的任务,直接删除TCB
{

                --uxCurrentNumberOfTasks;

                prvDeleteTCB( pxTCB );
}

/* uxTaskNumber
1,内核通知调试人员任务列表需要重新生成?? */
uxTaskNumber++;

 traceTASK_DELETE( pxTCB
);//nothing

}

taskEXIT_CRITICAL();//
退出临界段
/*
如果删除的是当前运行的任务,强迫进行任务调度 */
if( xSchedulerRunning != pdFALSE )//
调度器已启动
{

if( pxTCB == pxCurrentTCB )//
删除当前运行的任务
{

      configASSERT(uxSchedulerSuspended == 0 );//
调度器不能挂起

                /* 预删除钩子主要用于窗口模拟器,进行针对窗口的清除操作 */

                portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );//nothing

                portYIELD_WITHIN_API();//提交PendSV异常以切换任务
}

            else//删除的不是当前运行的任务
{

                /* 重设下一个任务阻塞时间xNextTaskUnblockTime */

                taskENTER_CRITICAL();//进入临界段

                {
prvResetNextTaskUnblockTime();

                }

                taskEXIT_CRITICAL();//退出临界段
}

}

    }

3.4.6.1     prvGetTCBFromHandle( pxHandle )

这是一个宏定义的操作,用于提取传递的任务句柄,将其类型转换成TCB_t类型的指针。如果传递的句柄为NULL,将返回当前任务的指针。

                

pxHandle 任务句柄

         返回任务的TCB指针

#define  prvGetTCBFromHandle( pxHandle )              ( ( ( pxHandle ) == NULL ) ? ( TCB_t * )pxCurrentTCB : ( TCB_t * ) ( pxHandle ) )

3.4.6.2     prvDeleteTCB(TCB_t *pxTCB )

删除任务。调用vPortFree()回收任务TCB和栈占用的内存。

                

pxTCB       任务的TCB指针

        
static void prvDeleteTCB( TCB_t *pxTCB )

    {
portCLEAN_UP_TCB( pxTCB );//nothing

#if( ( configSUPPORT_DYNAMIC_ALLOCATION/*1*/ == 1 ) && (configSUPPORT_STATIC_ALLOCATION/*0*/ == 0 ) )

{

/*
释放TCB和栈的内存*/
vPortFree( pxTCB->pxStack );

vPortFree( pxTCB );

}

#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && (configSUPPORT_STATIC_ALLOCATION == 1 ) )

{

/*
如果创建时是动态分配内存,释放TCB和栈的内存 */
if( pxTCB->ucStaticallyAllocated == ( uint8_t ) pdFALSE )

{

                vPortFree( pxTCB->pxStack );

                vPortFree( pxTCB );
}

else

{

mtCOVERAGE_TEST_MARKER();//nothing

}

}

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

    }

3.4.6.3     prvResetNextTaskUnblockTime(void )

重置xNextTaskUnblockTime的时间。该函数被内核多个函数调用,其作用是从延时列表中获取第一个任务解除阻塞的时间,用来更新xNextTaskUnblockTime

                

    

        

static void prvResetNextTaskUnblockTime(void )

{

TCB_t *pxTCB;
/*
延时任务列表是否为空 */
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )//
pdTRUE,就是空的

    {
/*
新的当前延时列表是空的,说明没有任务需要延时。将xNextTaskUnblockTime    
*
设为可能的最大值,因此任务调度时将跳过xTickCount >= xNextTaskUnblockTime检查 */
xNextTaskUnblockTime = portMAX_DELAY;//0xffffffffUL

    }
else

    {
/*
当前新的延时列表不是空的,获取列表头Item的值。 */
( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList);

xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB)->xStateListItem ) );

    }

}

3.4.7         taskDISABLE_INTERRUPTS()

这是一个宏定义,定义位于task.h文件中,调用宏portDISABLE_INTERRUPTS(),进而调用vPortRaiseBASEPRI()也是一个宏定义,位于portmacro.h文件中。用于屏蔽系统中断,其最终执行过程与处理器有关。

                

    

        

#define taskDISABLE_INTERRUPTS()    portDISABLE_INTERRUPTS()

3.4.8         taskENABLE_INTERRUPTS()

这也是一个宏定义,定义位于task.h文件中。与taskDISABLE_INTERRUPTS()相对应,调用portENABLE_INTERRUPTS(),进而调用vPortSetBASEPRI()用于开启系统中断,其最终执行过程与处理器有关。

                

    

        

#define taskENABLE_INTERRUPTS()     portENABLE_INTERRUPTS()

3.4.9         taskENTER_CRITICAL()

宏定义,定义位于task.h文件中。调用宏portENTER_CRITICAL(),进而调用vPortEnterCritical(),系统操作进入临界段,与taskEXIT_CRITICAL()配合使用。

                

    

        
#define taskENTER_CRITICAL()        portENTER_CRITICAL()

3.4.10       taskENTER_CRITICAL_FROM_ISR()

宏定义,中断服务中调用宏portSET_INTERRUPT_MASK_FROM_ISR(),进而调用ulPortRaiseBASEPRI()进入临界段。

                

    

        

#define taskENTER_CRITICAL_FROM_ISR()          portSET_INTERRUPT_MASK_FROM_ISR()

3.4.11       taskEXIT_CRITICAL()

宏定义,调用portEXIT_CRITICAL(),进而调用vPortExitCritical()退出临界段。

                

    

        

#define taskEXIT_CRITICAL()         portEXIT_CRITICAL()

3.4.12       taskEXIT_CRITICAL_FROM_ISR(x )

宏定义,中断服务中调用portCLEAR_INTERRUPT_MASK_FROM_ISR(),进而调用vPortSetBASEPRI()退出临界段。

                

    

        

#define taskEXIT_CRITICAL_FROM_ISR( x )          portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

3.4.13       xTaskGetApplicationTaskTag(TaskHandle_t xTask )

获取给定任务的钩子函数,如果传递的参数为NULL,则获取正在运行的任务的钩子函数。

                

xTask        任务句柄

         钩子函数的指针
TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask )

    {   
TCB_t *xTCB;
 
TaskHookFunction_t xReturn;
   
if( xTask == NULL )
     
{
   
xTCB = ( TCB_t * ) pxCurrentTCB;
   
}
      
else
  
{

xTCB = ( TCB_t * ) xTask;

}


taskENTER_CRITICAL();

{

xReturn = xTCB->pxTaskTag;

        }
taskEXIT_CRITICAL();

return xReturn;

    }


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

返回顶部