【FreeRTOS】学习笔记(五)

本文详细解析了动态创建任务、任务删除以及静态任务创建的源码实现过程,涉及内存分配、任务初始化、列表操作和任务调度策略,帮助读者掌握嵌入式编程中任务管理的核心机制。
摘要由CSDN通过智能技术生成

1. 动态创建任务实现(了解)

动态创建任务源码的实现过程:

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 )
{
    TCB_t * pxNewTCB;
    BaseType_t xReturn;

    /* 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. */
    #if ( portSTACK_GROWTH > 0 )
        {
            /* Allocate space for the TCB.  Where the memory comes from depends on
             * the implementation of the port malloc function and whether or not static
             * allocation is being used. */
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )
            {
                /* Allocate space for the stack used by the task being created.
                 * The base of the stack memory stored in the TCB so the task can
                 * be deleted later if required. */
                pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

                if( pxNewTCB->pxStack == NULL )
                {
                    /* Could not allocate the stack.  Delete the allocated TCB. */
                    vPortFree( pxNewTCB );
                    pxNewTCB = NULL;
                }
            }
        }
    #else /* portSTACK_GROWTH */
        {
            StackType_t * pxStack;

            /* Allocate space for the stack used by the task being created. */
            pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */

            if( pxStack != NULL )
            {
                /* Allocate space for the TCB. */
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */

                if( pxNewTCB != NULL )
                {
                    /* Store the stack location in the TCB. */
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                    /* The stack cannot be used as the TCB was not created.  Free
                     * it again. */
                    vPortFreeStack( pxStack );
                }
            }
            else
            {
                pxNewTCB = NULL;
            }
        }
    #endif /* portSTACK_GROWTH */

    if( pxNewTCB != NULL )
    {
        #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
            {
                /* Tasks can be created statically or dynamically, so note this
                 * task was created dynamically in case it is later deleted. */
                pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
            }
        #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */

        prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
        prvAddNewTaskToReadyList( pxNewTCB );
        xReturn = pdPASS;
    }
    else
    {
        xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
    }

    return xReturn;
}

总结一下步骤:

  1. 申请堆栈内存(返回首地址)
  2. 申请任务控制块内存(返回首地址)
  3. 把前面申请的堆栈地址,赋值给控制块的堆栈成员
  4. 调用prvInitialiseNewTask初始化任务控制块中的成员
    • 初始化堆栈为0xa5(可选)
    • 记录栈顶,保存在pxTopOfStack
    • 保存任务名字到pxNewTCB->pcTaskName[x]中
    • 保存任务优先级到pxNewTCB->uxPriority
    • 设置状态列表项的所属控制块,设置时间列表项的值
    • 列表项的插入是从小到大插入,所以这里将越高优先级的任务他的事件列表项值设置越小,这样就可以排到前面
    • 将任务句柄等于任务控制块
  5. 调用prvAddNewTaskToReadyList添加新创建任务到就绪列表中
    • 记录任务数量uxCurrentNumberOfTasks++
    • 判断新创建的任务是否为第一个任务
      • 如果创建的是第一个任务,初始化任务列表prvInitialiseTaskLists
      • 如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的任务
        优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块
    • 将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList
      • 将uxTopReadyPriority相应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就将该变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在
      • 将新创建的任务插入对应的就绪列表末尾
    • 如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换

2. 任务删除的实现(了解)

任务删除的源码实现过程

void vTaskDelete( TaskHandle_t xTaskToDelete )
{
    TCB_t * pxTCB;

    taskENTER_CRITICAL();
    {
        /* If null is passed in here then it is the calling task that is
         * being deleted. */
        pxTCB = prvGetTCBFromHandle( xTaskToDelete );

        /* Remove task from the ready/delayed 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();
        }

        /* 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( pxTCB == pxCurrentTCB )
        {
            /* A task is deleting itself.  This cannot complete within the
             * task itself, 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. */
            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. */
            ++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. */
            portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
        }
        else
        {
            --uxCurrentNumberOfTasks;
            traceTASK_DELETE( pxTCB );

            /* Reset the next expected unblock time in case it referred to
             * the task that has just been deleted. */
            prvResetNextTaskUnblockTime();
        }
    }
    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. */
    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();
        }
    }
}

总结一下步骤:

  1. 获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
  2. 将被删除任务,移除所在列表:将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表
  3. 判断所需要删除的任务
    • 删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行
    • 删除其他任务,当前任务数量–:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时任务。
  4. 删除的任务为其他任务则直接释放内存prvDeleteTCB()
  5. 调度器正在运行且删除任务自身,则需要进行一次任务切换

3. 静态创建任务实现(了解)

静态创建任务不常用,其内部实现步骤可总结为:

  1. 获取控制块内存(首地址)
  2. 获取堆栈内存(首地址)
  3. 标记使用的静态的方式申请的TCB和堆栈内存
  4. 调用prvInitialiseNewTask 初始化任务块,并将控制块信息返回给任务句柄,以便后续返回句柄信息
  5. 调用prvAddNewTaskToReadyList 添加新创建任务到就绪列表中
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值