4. FreeRTOS系统任务的删除与空闲任务

        下面是对删除任务函数、空闲任务、相对延时函数的一些应用与说明。

第一节 Delete任务

        任务的删除需要使用vTaskDelete()函数,主要分为两种删除情况:自删除与强制删除。
        自删除表示在任务本身的执行中调用 vTaskDelete(NULL)来删除自身。
        强制删除表示在其他任务BB中,删除另一个任务AA vTaskDelete(AA)。

        A. 当删除一个任务时,则该任务将在FreeRTOS系统中完全消失,所以需要将任务从列表中(就绪,阻塞,挂起和事件链表)中移除。
        B. 任务删除也要删除任务的信息,即任务结构体TCB。(前提任务是动态创建的,TCB与任务的栈都是动态分配的也就是说可以回收)
        C. 任务的栈中还保存着任务函数那些局部变量等,任务删除一样也要释放任务的栈
        D. 当一个正在运行的任务调用了vTaskDelete(NULL)删除自己时,只会将任务的从列表(就绪,阻塞,挂起和事件链表)中移除,而任务结构体TCB与栈会交给空闲任务去释放,这样才算完成任务的删除。
void vTaskDelete( TaskHandle_t xTaskToDelete )  //传入的参数是要删除任务对应的句柄
{
        TCB_t *pxTCB;
        taskENTER_CRITICAL();  //关闭中断
        {
                pxTCB = prvGetTCBFromHandle( xTaskToDelete ); //获取要删除的任务控制块,NULL代表任务自身
                if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )   //从就绪(延时)列表中移除任务
                {
                        taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                }
                if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )  //从就事件列表中移除任务
                {
                        ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
                 }

            /* 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 )
               {
                        /*下面是任务删除自身的情况,任务删除自身时需要先放入等待删除的列表中,等切换到空闲任务时,空闲任务会将等待删除的列表中的任务控制块与堆栈进行释放*/
                        vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
                        ++uxDeletedTasksWaitingCleanUp;
                        portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
                }
                else
               {     
                        /*删除其他任务*/
                         --uxCurrentNumberOfTasks;
                        prvDeleteTCB( pxTCB );  //用来删除任务块与任务的栈空间
                        prvResetNextTaskUnblockTime(); //重置下一个任务的解除阻塞事件
                }

                traceTASK_DELETE( pxTCB );
        }
        taskEXIT_CRITICAL(); //打开中断

        /* 如果任务删除自身,则需要下面的code来进行一次任务切换*/
        if( xSchedulerRunning != pdFALSE )
        {
            if( pxTCB == pxCurrentTCB )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }

第二节 空闲任务

        当FreeRTOS的任务调度器开始工作后,为了保证至少有一个任务在运行,空闲任务被自动创建的目的就是要保证FreeRTOS系统中必须至少有一个任务处于运行状,空闲任务占用最低优先级。

        空闲任务的主要作用:

        1. 释放被删除任务的内存
         如果一个任务删除自身,则任务的堆栈和任务控制块TCB需要通过空闲任务删除。所以空闲任务开始就会检查是否有任务删除了自己,空闲任务负责删除此任务的TCB和堆栈空间。(如果一个任务删除另外一个任务,被删除任务的堆栈和TCB立即释放。)
        2. 处理空闲优先级任务
         当使用抢占式内核调度时,如果有任务与空闲任务共享一个优先级,并且宏configIDLE_SHOULD_YIELD设置为1,那么空闲任务不必等到时间片耗尽再进行任务切换。
        用户任务使用空闲优先级,空闲任务检查空闲优先级下的就绪列表中是否有多个任务,有的话则执行任务切换,让用户任务获得CPU权限。
        3. 运行用户设置的空闲任务钩子函数
        设置了configUSE_IDLE_HOOK == 1正常空闲任务钩子函数,这个钩子函数由用户来编写。钩子函数不可以调用会引起空闲任务阻塞的API函数(例如:vTaskDelay()、带有阻塞时间的队列和信号量函数),在钩子函数内部使用协程是被允许的。使用空闲钩子函数设置CPU进入省电模式是很常见的。

        4. 支持低功耗tickless模式

第三节 相对延时函数

        相对延时是指每次延时都是从执行相对延时函数vTaskDelay()开始,直到延时指定的时间。在FreeRTOS系统中延时阻塞用的非常频繁,因为低优先级的任务想要得到运行,那么高优先级的任务就必须进入阻塞或者挂起的状态,所以高优先级的任务一定会有阻塞的情况。(一般可以调用vTaskDelay()或者vTaskDelayUntil(),将CPU使用权让给低优先级的任务)。
        每次系统节拍时钟中断,中断服务函数都会检查这两个延时列表,查看延时的任务是否到期,如果时间到期,则将任务从延时列表中删除,重新加入就绪列表。如果新加入就绪列表的任务优先级大于当前任务,则会触发一次上下文切换。

void vTaskDelay( const TickType_t xTicksToDelay )
{

        BaseType_t xAlreadyYielded = pdFALSE;        
        if( xTicksToDelay > ( TickType_t ) 0U )
        {
                configASSERT( uxSchedulerSuspended == 0 ); 
                 vTaskSuspendAll(); //挂起任务调度器
            {
                traceTASK_DELAY();
                prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); //将当前任务放入延迟列表中,经过延时时间后在移除延时列表
            }
            xAlreadyYielded = xTaskResumeAll(); //开启任务调度器
        }
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API(); //因为当前任务别放入了延时列表中,所以要进行任务切换来运行其他任务
        }
    }

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值