首页 > 其他 > 详细

FreeRTOS Task Management(3)- 任务创建与删除

时间:2021-06-17 12:06:12      阅读:15      评论:0      收藏:0      [点我收藏+]

FreeRTOS Task Management(3)- 任务创建与删除

/* FreeRTOS Kernel V10.4.1 */
/* 为了便于描述,源码中去除了一些用于调试的语句,如常见的trace_XXX, mtCOVERAGE_TEST_MARKER()等 */

原文链接:https://www.cnblogs.com/yanpio/p/14892113.html

1 任务创建

FreeRTOS提供了一系列的任务创建函数,主要的区别是创建任务时,内存分配方式存在差异。下表对比了几种任务创建函数的内存分配方式,从源码中可以具体了解到这些不同之处。

技术分享图片

四种任务创建函数都会调用初始化函数prvInitialiseNewTask()和添加列表函数prvAddNewTaskToReadyList().以下首先介绍这两个函数。

1.1 prvInitialiseNewTask()

/* 初始化新的task,主要是对TCB进行操作。*/
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,/* task执行函数 */
                                  const char * const pcName, /* task名称 */
                                  const uint32_t ulStackDepth,/* task栈深度,单位是word,即 4 bytes */
                                  void * const pvParameters,/* 用于传递参数给task,可以指向一个简单变量,也可以指向一个结构体变量*/
                                  UBaseType_t uxPriority,/* 优先级 */
                                  TaskHandle_t * const pxCreatedTask,/* 可用于返回任务句柄 */
                                  TCB_t * pxNewTCB, /* TCB指针 */
                                  const MemoryRegion_t * const xRegions ) /* 如果使用内存保护单元(MPU),则会使用该变量*/
{
    StackType_t * pxTopOfStack;
    UBaseType_t x;

    /* 如果使用 portUSING_MPU_WRAPPERS,则进行如下操作。对此不太理解,因此后文默认 portUSING_MPU_WRAPPERS = 0 */
    #if ( portUSING_MPU_WRAPPERS == 1 )
        /* Should the task be created in privileged mode? */
        BaseType_t xRunPrivileged;

        if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
        {
            xRunPrivileged = pdTRUE;
        }
        else
        {
            xRunPrivileged = pdFALSE;
        }
        uxPriority &= ~portPRIVILEGE_BIT;
    #endif /* portUSING_MPU_WRAPPERS == 1 */

    /* 如果开启选择,则将stack初始化为固定值0xa5(可用于调试). */
    #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
    {
          ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
     }
    #endif 

    /* 根据stack生长的方向,来进行stack初始化. */
    #if ( portSTACK_GROWTH < 0 )
        {
            /* 如果stack向下生长,则栈顶在高地址 */
            pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
            /* 栈顶地址对齐,此操作在FreeRTOS Memory Management 有较为详细的分析 */
            pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 

            /* 检查对齐是否OK */
            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

            /* 记录栈空间最高位地址 */
            #if ( configRECORD_STACK_HIGH_ADDRESS == 1 )
                {
                    pxNewTCB->pxEndOfStack = pxTopOfStack;
                }
            #endif 
        }
    #else /* 如果stack向上生长,则栈顶在低地址 */
        {
            pxTopOfStack = pxNewTCB->pxStack;

            /* 检查对齐情况 */
            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

            /* 记录栈空间最高地址 */
            pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
        }
    #endif 

    /* 保存task name. */
    if( pcName != NULL )
    {
        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
            pxNewTCB->pcTaskName[ x ] = pcName[ x ];

            /* 不能直接复制configMAX_TASK_NAME_LEN,因为可能name的实际长度没有这么长,并且实际长度后的内存访问情况不确定 */
            if( pcName[ x ] == ( char ) 0x00 )
            {
                break;
            }           
        }

        /* 加上结束符‘\0‘. */
        pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = ‘\0‘;
    }
    else
    {
        pxNewTCB->pcTaskName[ 0 ] = 0x00;
    }

    /* 优先级值要在合理范围内. */
    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }
   
    /* 记录优先级相关的变量,可参考上一篇文章中对TCB结构的相关描述 */
    pxNewTCB->uxPriority = uxPriority;
    #if ( configUSE_MUTEXES == 1 )
        {
            pxNewTCB->uxBasePriority = uxPriority;
            pxNewTCB->uxMutexesHeld = 0;
        }
    #endif 

    /* 初始化xStateListItem 和 xEventListItem(将item的pxContainer指针置为NULL) */
    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

    /* 设置xStateListItem 的 owner 为当前TCB. */
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

    /* 设置 xEventListItem的值,设置为 configMAX_PRIORITIES - uxPriority. 优先级高的(数值大的)在数组前面。 */
    listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); 
    /* 设置xEventListItem 的 owner 为当前TCB. */
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

    /* 将临界区嵌套深度初始化为0 */
    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        {
            pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
        }
    #endif 

    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        {
            pxNewTCB->pxTaskTag = NULL;
        }
    #endif 

    /* 运行计数初始化为0 */
    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        {
            pxNewTCB->ulRunTimeCounter = 0UL;
        }
    #endif 

    /* 如果开启portUSING_MPU_WRAPPERS, 则保存MPUSettings */
    #if ( portUSING_MPU_WRAPPERS == 1 )
        {
            vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
        }
    #else
        {
            ( void ) xRegions;
        }
    #endif

    /* 将本地参数指针内容清空 */
    #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
        {
            memset( ( void * ) &( pxNewTCB->pvThreadLocalStoragePointers[ 0 ] ), 0x00, sizeof( pxNewTCB->pvThreadLocalStoragePointers ) );
        }
    #endif

    /* 将任务通知相关的变量清空,后续会更详细说明  */
    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        {
            memset( ( void * ) &( pxNewTCB->ulNotifiedValue[ 0 ] ), 0x00, sizeof( pxNewTCB->ulNotifiedValue ) );
            memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) );
        }
    #endif

    /* newlib 相关初始化 */
    #if ( configUSE_NEWLIB_REENTRANT == 1 )
        {
            _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
        }
    #endif

    #if ( INCLUDE_xTaskAbortDelay == 1 )
        {
            pxNewTCB->ucDelayAborted = pdFALSE;
        }
    #endif

    /* 为了方便描述,以下对源码进行一些简化,
     * 假设 portUSING_MPU_WRAPPERS = 0 ,portSTACK_GROWTH = -1,portHAS_STACK_OVERFLOW_CHECKING = 1 
     * portHAS_STACK_OVERFLOW_CHECKING值标志 port 是否具有stack overflow 检查的功能
     * pxPortInitialiseStack()功能根据port不同而异,一般是初始化栈空间中的一些寄存器值
     */
      pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
   
    /* 如果任务创建成功,则通过输入指针返回任务句柄,用于任务的其他操作(修改优先级,删除任务等) */
    if( pxCreatedTask != NULL )
    {
        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
    }
}

1.2 prvAddNewTaskToReadyList()

static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
    /* 更新ready list时,需要保证不被打扰.因此使用taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 进行保护*/
    taskENTER_CRITICAL();
    {
        /* task 数量递增 */
        uxCurrentNumberOfTasks++;

        if( pxCurrentTCB == NULL )
        {
            /* 如果 pxCurrentTCB == NULL,则意味着还没有任务,或者所有任务都被挂起了. */
            pxCurrentTCB = pxNewTCB;

            /* 如果任务数量为1,则需要进行初始化。因为调度器会创建一个idle task,
             * 因此即使有任务的增删操作,uxCurrentNumberOfTasks的数值不会再次变为1,初始化操作也只会进行一次 */
            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
            {
                /* 详见本节(下面),关于 prvInitialiseTaskLists() 的分析 */
                prvInitialiseTaskLists();
            }
        }
        else
        {
            /* 如果调度器不在运行,新加入的优先级更高,则将pxCurrentTCB的值设置为pxNewTCB。等调度器恢复后,则直接运行优先级较高的task */
            if( xSchedulerRunning == pdFALSE )
            {
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    pxCurrentTCB = pxNewTCB;
                }        
            }           
        }

        uxTaskNumber++;

        #if ( configUSE_TRACE_FACILITY == 1 )
            {
                /* uxTCBNumber(TCB序号)赋值,用于跟踪调试. */
                pxNewTCB->uxTCBNumber = uxTaskNumber;
            }
        #endif
       
        /* 详见本节(后面)关于 prvAddTaskToReadyList()的分析*/
        prvAddTaskToReadyList( pxNewTCB );

        portSETUP_TCB( pxNewTCB );/* #define portSETUP_TCB( pxTCB )    ( void ) pxTCB */
    }
    taskEXIT_CRITICAL();

    if( xSchedulerRunning != pdFALSE )
    {
        /* 如果调度器正在运行,并且新加入的task优先级比当前运行的task优先级高,则当前task退出,重新调度. */
        if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
        {
            taskYIELD_IF_USING_PREEMPTION();
        }       
    }
}

/*---------------------------------------------------------------------------------------------------------*/

/* 初始化任务列表 */
static void prvInitialiseTaskLists( void )
{
    UBaseType_t uxPriority;

    /* 按照优先级,对pxReadyTasksLists数组逐个进行初始化。 */
    for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        /* vListInitialise 函数详见 < FreeRTOS Task Management(1)- list实现 > */
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
    }

    /* 初始化各个全局的list */
    vListInitialise( &xDelayedTaskList1 );
    vListInitialise( &xDelayedTaskList2 );
    vListInitialise( &xPendingReadyList );

    #if ( INCLUDE_vTaskDelete == 1 )
        {
            vListInitialise( &xTasksWaitingTermination );
        }
    #endif 

    #if ( INCLUDE_vTaskSuspend == 1 )
        {
            vListInitialise( &xSuspendedTaskList );
        }
    #endif 

    /* 指针指向对应的list,用于taskDelay相关操作. */
    pxDelayedTaskList = &xDelayedTaskList1;
    pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

/*---------------------------------------------------------------------------------------------------------*/

/* 主要分两步,一是记录当前最高的优先级(如果pxTCB的优先级更高), 
 * 二是将xStateListItem插入到对应优先级的pxReadyTasksLists中,这样调度器就可以进行调度 */
#define prvAddTaskToReadyList( pxTCB )                                                                     taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                                                    vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); 
    
/*---------------------------------------------------------------------------------------------------------*/

1.3 xTaskCreate()

/* xTaskCreate 是经常使用的一种 <动态> 创建任务的方式。仅在configSUPPORT_DYNAMIC_ALLOCATION == 1,才能进行调用 */

#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

    BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName,
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
    {
        TCB_t * pxNewTCB;
        BaseType_t xReturn;

        /* 由于是动态分配,因此需要在创建任务时 分配 stack 和 TCB的空间。
         * 根据portSTACK_GROWTH(栈生产方向)的不同,需要注意stack和tcb的分配顺序,以确定stack生长不会影响到TCB。*/
        
        /* 栈向高地址生长,则先分配TCB,再分配stack */
        #if ( portSTACK_GROWTH > 0 )
            {
                /* pvPortMalloc详细介绍见FreeRTOS Memory Management 系列博客 */
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

                /* 如果TCB 和 stack 分配都OK了,则继续进行初始化操作*/
                if( pxNewTCB != NULL )
                {   
                    pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 
                    if( pxNewTCB->pxStack == NULL )
                    {
                        vPortFree( pxNewTCB );
                        pxNewTCB = NULL;
                    }
                }
            }       
        #else  /* 栈向低地址生长,则先分配stack,再分配TCB */
            {
                StackType_t * pxStack;
                pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 
                
                if( pxStack != NULL )
                {
                    
                    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
                    
                    if( pxNewTCB != NULL )
                    { 
                        pxNewTCB->pxStack = pxStack;
                    }
                    else
                    {  
                        vPortFree( pxStack );
                    }
                }
                else
                {
                    pxNewTCB = NULL;
                }
            }
        #endif 

        if( pxNewTCB != NULL )
        {
            #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
                {
                    /* stack 和 TCB 都是动态分配,在删除任务时都需要进行free,详见2.2节 */
                    pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
                }
            #endif 

            /* 初始化操作,见上述3.1节 和 3.2节的详细分析 */
            prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;
        }
        else
        {
            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
        }

        return xReturn;
    }

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

1.4 xTaskCreateStatic()

/* xTaskCreateStatic 函数的特点是静态分配TCB 和stack */
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

    TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                    const char * const pcName,
                                    const uint32_t ulStackDepth,
                                    void * const pvParameters,
                                    UBaseType_t uxPriority,
                                    StackType_t * const puxStackBuffer, /* 分配好的stack 空间 */
                                    StaticTask_t * const pxTaskBuffer ) /* 分配好的TCB空间 */
    {
        TCB_t * pxNewTCB;
        TaskHandle_t xReturn;

        /* 进行一波检查,以确定输入的空间地址和大小是否正常 */
        configASSERT( puxStackBuffer != NULL );
        configASSERT( pxTaskBuffer != NULL );

        #if ( configASSERT_DEFINED == 1 )
            {
                volatile size_t xSize = sizeof( StaticTask_t );
                configASSERT( xSize == sizeof( TCB_t ) );
                ( void ) xSize; 
            }
        #endif 

        /* 如果上述检查没有问题,则对pxNewTCB 和 pxNewTCB->pxStack进行赋值 */
        if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
        {
            pxNewTCB = ( TCB_t * ) pxTaskBuffer; 
            pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;

            #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
                {
                    /* stack 和 TCB 都是静态分配,在删除任务时都不需要进行free,详见2.2节 */
                    pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
                }
            #endif 

            /* 初始化操作,见上述3.1节 和 3.2节的详细分析 */
            prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
            prvAddNewTaskToReadyList( pxNewTCB );
        }
        else
        {
            xReturn = NULL;
        }

        return xReturn;
    }

#endif 

1.5 xTaskCreateRestrictedStatic()

/* 仅在实现了MPU的系统上,才能调用此函数 */
/* 
 *   typedef struct xTASK_PARAMETERS
 *   {
 *       TaskFunction_t pvTaskCode;
 *       const char * const pcName;     
 *       configSTACK_DEPTH_TYPE usStackDepth;
 *       void * pvParameters;
 *       UBaseType_t uxPriority;
 *       StackType_t * puxStackBuffer;
 *       MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ];
 *       #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
 *           StaticTask_t * const pxTaskBuffer;
 *       #endif
 *    } TaskParameters_t;
 */

#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )

    /* pxTaskDefinition参数的定义如上,除了xTaskCreate函数的输入参数之外,还包含了xRegions,用于prvInitialiseNewTask函数进行初始化。
     * 函数实现与xTaskCreateStatic基本一致  */ 
    BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,
                                            TaskHandle_t * pxCreatedTask ) /* 创建的task句柄 */
    {
        TCB_t * pxNewTCB;
        BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

        configASSERT( pxTaskDefinition->puxStackBuffer != NULL );
        configASSERT( pxTaskDefinition->pxTaskBuffer != NULL );

        if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) )
        {
            pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer;
            pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer;

            #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
                {
                   /* stack 和 TCB 都是静态分配,在删除任务时都不需要进行free,详见2.2节 */
                    pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
                }
            #endif

            /* 初始化操作,见上述3.1节 和 3.2节的详细分析 */
            prvInitialiseNewTask( pxTaskDefinition->pvTaskCode,
                                  pxTaskDefinition->pcName,
                                  ( uint32_t ) pxTaskDefinition->usStackDepth,
                                  pxTaskDefinition->pvParameters,
                                  pxTaskDefinition->uxPriority,
                                  pxCreatedTask, pxNewTCB,
                                  pxTaskDefinition->xRegions );/* 在portUSING_MPU_WRAPPERS == 0时 ,此参数输入NULL */

            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;
        }

        return xReturn;
    }

#endif 

1.6 xTaskCreateRestricted()

#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )

    BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
                                      TaskHandle_t * pxCreatedTask )
    {
        TCB_t * pxNewTCB;
        BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

        configASSERT( pxTaskDefinition->puxStackBuffer );

        if( pxTaskDefinition->puxStackBuffer != NULL )
        {
            /* 分配TCB. */
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )
            {
                /* 从输入参数中获取stack空间 */
                pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer;

                #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
                    {
                       /* stack 是静态分配,在删除任务时需要free TCB,详见2.2节 */
                        pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY;
                    }
                #endif 

                /* 初始化操作,见上述3.1节 和 3.2节的详细分析 */
                prvInitialiseNewTask( pxTaskDefinition->pvTaskCode,
                                      pxTaskDefinition->pcName,
                                      ( uint32_t ) pxTaskDefinition->usStackDepth,
                                      pxTaskDefinition->pvParameters,
                                      pxTaskDefinition->uxPriority,
                                      pxCreatedTask, pxNewTCB,
                                      pxTaskDefinition->xRegions );/* 在portUSING_MPU_WRAPPERS == 0时 ,此参数输入NULL */

                prvAddNewTaskToReadyList( pxNewTCB );
                xReturn = pdPASS;
            }
        }

        return xReturn;
    }

#endif 

2 任务删除

2.1 vTaskDelete()

#if ( INCLUDE_vTaskDelete == 1 )
 
    /* 删除任务主要是将TCB中包含的各种ListItem从list中移除,并释放资源 */

    void vTaskDelete( TaskHandle_t xTaskToDelete )
    {
        TCB_t * pxTCB;

        /* 删除过程要进行保护,防止中断打断 */
        taskENTER_CRITICAL();
        {
            /* 获取句柄对应的TCB. */
            pxTCB = prvGetTCBFromHandle( xTaskToDelete );

            /* 将task从 ready/delayed list中移除 */
            if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
            {
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }
           
            /* 如果task在等待事件,则从事件列表中移除该task */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            
            /* 需要注意,此值是一个静态全局变量,与TCB结构体中的uxTaskNumber不是一个东西。该值在任务数量发生变化时(创建或删除)都会+1,
             * 并以此提醒调度器,任务数量已经发生变化,需要重新生成任务列表 */
            uxTaskNumber++;

            /* 如果被删除的是当前正在执行的Task,则需要idle task 去执行清理操作。因为task无法释放自己的资源。
             * 需要的做的是将任务添加到待删除列表中  */
            if( pxTCB == pxCurrentTCB )
            {
                /* 将需要删除的task添加到删除列表,等待idle task去做后续的清理工作,详见2.4节 */
                vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

                /* 待清除任务计数加1. */
                ++uxDeletedTasksWaitingCleanUp;
            }
            else /* 如果不是当前正在执行的Task,则直接进行清理即可 */
            {
                /* 当前task数量减1。需要指出的是,在pxTCB == pxCurrentTCB时,并没有直接减1,
                 * 是因为在idle task 里进行了减1 操作,详见2.4节 */
                --uxCurrentNumberOfTasks;
                
                /* 见2.2 节详细分析,主要工作是释放内存 */
                prvDeleteTCB( pxTCB );

                /* 重置延时时间,详见2.3节. */
                prvResetNextTaskUnblockTime();
            }
        }
        taskEXIT_CRITICAL();

        /* 如果调度器在运作,并且当前的Task被删除,则需要主动释放执行权,调度器重新调度. */
        if( xSchedulerRunning != pdFALSE )
        {
            if( pxTCB == pxCurrentTCB )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }  
        }
    }

#endif 

2.2 prvDeleteTCB()

static void prvDeleteTCB( TCB_t * pxTCB )
    {
        /* #define portCLEAN_UP_TCB( pxTCB )    ( void ) pxTCB */ 
        portCLEAN_UP_TCB( pxTCB ); 

        /* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
         * for additional information. */
        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            {
                _reclaim_reent( &( pxTCB->xNewLib_reent ) );
            }
        #endif 

        /* 参照1中的表格,以下这种搭配,TCB和stack都需要释放 */
        #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
            {
                vPortFree( pxTCB->pxStack );
                vPortFree( pxTCB );
            }
        /* 仅在tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0时,才会记录pxTCB->ucStaticallyAllocated的值 */
        #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
            {
                /* 1.3节中的xTaskCreate函数的内存分配方式是 tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB */
                if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
                {
                    /* TCB 和 stack都是动态分配的,因此都需要free */
                    vPortFree( pxTCB->pxStack );
                    vPortFree( pxTCB );
                }
                
                /* 1.4节中xTaskCreateRestrictedStatic函数和1.5节中xTaskCreateRestrictedStatic函数都是                    
                 * tskSTATICALLY_ALLOCATED_STACK_ONLY. */
                else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
                {
                    /* 仅有stack是静态分配,因此TCB需要free */
                    vPortFree( pxTCB );
                }
                
                /*1.6节中的 xTaskCreateRestricted函数内存分配方式是tskSTATICALLY_ALLOCATED_STACK_AND_TCB */
                else
                {
                    /* TCB 和 stack都是静态分配的,因此不需要free  */
                    configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB );
                }
            }
        #endif 
    }

2.3 prvResetNextTaskUnblockTime()

/* 此函数涉及到任务延时,此处简单说明,会在后续的文章中详细描述 task delay */
static void prvResetNextTaskUnblockTime( void )
{
    if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
    {
        /* 删除task时,已经将其从delayed list 中删除。如果此时list已经为空,则将xNextTaskUnblockTime 设置为最大值 */
        xNextTaskUnblockTime = portMAX_DELAY;
    }
    else
    {
        /* 如果list不为空,则将xNextTaskUnblockTime的值设置为下一个即将执行的task的delay time. */
        xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList );
    }
}

2.4 prvCheckTasksWaitingTermination()

static void prvCheckTasksWaitingTermination( void )
{
    /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/
    
    TCB_t * pxTCB;
    /* 当有任务需要清除时,就逐个进行清除 */
    while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U )
    {
        taskENTER_CRITICAL();
        {
            pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); 
            
            /* 执行清理工作 */
            ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
            --uxCurrentNumberOfTasks;
            --uxDeletedTasksWaitingCleanUp;
        }
        taskEXIT_CRITICAL();
        
        /* 详见2.2节 */
        prvDeleteTCB( pxTCB );
    }
}

FreeRTOS Task Management(3)- 任务创建与删除

原文:https://www.cnblogs.com/yanpio/p/14892113.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!