FreeRTOS学习记录----任务删除、挂起、恢复函数详解


(一)任务删除函数详解

  vTaskDelete()函数用于删除一个任务,形参为要删除任务的任务句柄,如果删除自身,那么参数为NULL。要想使用该函数,必须将宏INCLUDE_vTaskDelete定义为1;要删除的任务就是把任务从所有就绪列表,阻塞列表,挂起列表中删除。

直接上代码!

void vTaskDelete( TaskHandle_t xTaskToDelete ){
    TCB_t *pxTCB;
    taskENTER_CRITICAL();
    {
        /* 获取要删除任务的任务控制块,参数是任务句柄,如果参数为当前正在执行的任务句柄,那么返回值为null */
        pxTCB = prvGetTCBFromHandle( xTaskToDelete );
        /* 将任务从就绪列表中删除 */
        if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){
            taskRESET_READY_PRIORITY( pxTCB->uxPriority );
        }
        else{
            mtCOVERAGE_TEST_MARKER();
        }
        /* 查看任务是否在等待某个事件信号量,队列等,并将其从相应的列中删除 */
        if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){
            ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
        }
        else{
            mtCOVERAGE_TEST_MARKER();
        }
            
        uxTaskNumber++;
        /* 如果要删除的是当前正在运行的任务 */
        if( pxTCB == pxCurrentTCB ){
            /* 把任务添加到等待删除的任务列表中,并在空闲任务中删除 */
            vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
            /* 记录有多少个任务需要释放内存 */
            ++uxDeletedTasksWaitingCleanUp;
            /* 任务删除钩子函数---需要用户自己实现*/
            portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
        }
        else{
            /* 要删除的是别的任务 */
            --uxCurrentNumberOfTasks;
            prvDeleteTCB( pxTCB );
            /* 重新计算还要多长时间执行下一个任务 */
            prvResetNextTaskUnblockTime();
        }
        traceTASK_DELETE( pxTCB );
    }
    /* 退出临界段 */
    taskEXIT_CRITICAL();

    /* 如果任务调度器开启 */
    if( xSchedulerRunning != pdFALSE ){
        /* 如果是删除任务本身,马上进行任务调度)*/
        if( pxTCB == pxCurrentTCB ){
            configASSERT( uxSchedulerSuspended == 0 );
            portYIELD_WITHIN_API();
        }
        else{
            mtCOVERAGE_TEST_MARKER();
        }
    }
}

  具体的结构框图如下所示:

(二)任务挂起函数详解

  vTaskSuspend()函数用于挂起指定的任务,被挂起的任务失去cpu的使用权,不管是什么优先级。可以将任何状态的任务挂起。要用此函数,INCLUDE_vTaskDelete必须定义为1。

  直接上代码!

void vTaskSuspend(TaskHandle_t xTaskToSuspend){
    TCB_t *pxTCB;
    taskENTER_CRITICAL();
    {
        /* 获取任务控制块,若为NULL则挂起自身 */
        pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
        /* 将任务从就绪列表、阻塞列表等中移除 */
        if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0){
            taskRESET_READY_PRIORITY(pxTCB->uxPriority);
        }
        else{
            mtCOVERAGE_TEST_MARKER();
        }
        /* 查看任务是否在等待某个事件,如是则将其从事件列表中移除 */
        if(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem))!=NULL){
            (void) uxListRemove(&(pxTCB->xEventListItem));
        }
        else{
            mtCOVERAGE_TEST_MARKER();
        }
        /* 将任务添加到挂起任务列表表尾 */
        vListInsertEnd(&xSuspendedTaskList, &(pxTCB->xStateListItem));
    }
    taskEXIT_CRITICAL();
    
    if(xSchedulerRunning != pdFALSE){    //如果任务调度器开启
        /* 重新计算还要多长时间执行下一个任务 */
        taskENTER_CRITICAL();
        {
            prvResetNextTaskUnblockTime();
        }
        taskEXIT_CRITICAL();
    }
    else{
        mtCOVERAGE_TEST_MARKER();
    }

    if(pxTCB == pxCurrentTCB){
        if(xSchedulerRunning != pdFALSE){
            /* 若刚挂起的是正在运行的任务,且任务调度器运行正常,则强制进行一次任务切换 */
            configASSERT( uxSchedulerSuspended == 0 );
            portYIELD_WITHIN_API();
        }
        else{
            /* 若任务调度器没有开启,则读取当前任务挂起列表的长度,判断所有任务是否都被挂起*/
            if(listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == uxCurrentNumberOfTasks){
                /* 若所有任务都被挂起,把当前的任务控制块赋值为NULL    */
                pxCurrentTCB = NULL;
            }
            else{
                /* 若还有没被挂起的任务,则获取下一个要运行的任务 */
                vTaskSwitchContext();
            }
        }
    }
    else{
        mtCOVERAGE_TEST_MARKER();
    }
}

  可见任务挂起和任务删除函数有许多共同之处。

结构图如下,仔细分析哦

(三)任务恢复函数详解

  任务恢复就是让挂起的任务重新进入就绪列表,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续进行。如果被恢复的任务是 在所有就绪列表中的优先级最高,那么系统将进行任务上下文切换。

  直接上代码!

void vTaskResume(TaskHandle_t xTaskToResume){
    /* 获取要恢复的任务控制块 */
    TCB_t * const pxTCB = (TCB_t *) xTaskToResume;
    configASSERT( xTaskToResume );

    /* 任务控制块不能为NULL和当前任务    */
    if(( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB )){
        taskENTER_CRITICAL();
        {
            /* 判断要恢复的任务是否已经被挂起 */
            if(prvTaskIsTaskSuspended(pxTCB) != pdFALSE){
                /* 从挂起列表中移除 */
                (void) uxListRemove(&( pxTCB->xStateListItem));
                /* 添加到就绪列表中 */
                prvAddTaskToReadyList( pxTCB );
                /* 要恢复的任务优先级高于当前正在运行的任务优先级 */
                if(pxTCB->uxPriority >= pxCurrentTCB->uxPriority){
                    /* 完成一次任务切换 */
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else{
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();
    }
    else{
        mtCOVERAGE_TEST_MARKER();
    }
}

  首先要保证要恢复的任务不能是空并且不能是当前正在运行的任务,不然不需要恢复了,然后看下面的结构图,比对着代码就行了。

好!本章就先介绍到这里,下章介绍开启任务调度!