1.任务挂起
-
功能 :将指定任务挂起。被挂起的任务不再参与调度,直到被其他任务调用
vTaskResume()恢复。 -
参数 :
xTaskToSuspend是要挂起的任务句柄,若为NULL则挂起调用者自身。 -
注意 :挂起不同于删除,任务控制块和栈内存不会被释放 ,只是任务状态变为
Suspended,挂起期间保留所有资源,可以被唤醒void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{
TCB_t * pxTCB;taskENTER_CRITICAL(); //程序进入临界段 { /* If null is passed in here then it is the running task that is * being suspended. */ /* 若传入 NULL,返回当前任务(即自挂起),反之返回该任务 */ pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); traceTASK_SUSPEND( pxTCB ); //跟踪宏,用户可自定义用于参数检查,默认未实现 /* Remove task from the ready/delayed list and place in the * suspended list. */ /* 将该任务从就绪/延迟任务列表中删除 */ /* 注意:挂起任务会自动移除任何当前的阻塞延时(即延时状态被取消,但任务不会重新就绪,而是直接挂起) */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* 返回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(); //用于代码覆盖率测试的宏,此处等价于一个空语句 } /* 将任务的状态列表项插入全局挂起列表 xSuspendedTaskList 的末尾 * 插入末尾而非按优先级排序,因为挂起任务不参与调度,排序无意义,仅用作收纳 */ vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); /* 如果启用了任务通知功能,每个任务有 configTASK_NOTIFICATION_ARRAY_ENTRIES 个通知状态 */ #if ( configUSE_TASK_NOTIFICATIONS == 1 ) { BaseType_t x; for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) { /* 如果某通知状态为 taskWAITING_NOTIFICATION(即任务正等待一个通知而阻塞),由于任务将挂起,还没有收到通知, * 此处直接将状态改为 taskNOT_WAITING_NOTIFICATION,表示不再等待 */ if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) { /* The task was blocked to wait for a notification, but is * now suspended, so no notification was received. */ pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; } } } #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ } taskEXIT_CRITICAL(); //程序退出临界段 /* 如果调度器处于运行状态,则重新计算下一次任务解除阻塞的时间【临界段中运行,防止状态中途改变】 */ if( xSchedulerRunning != pdFALSE ) { /* Reset the next expected unblock time in case it referred to the * task that is now in the Suspended state. */ taskENTER_CRITICAL(); //程序进入临界段 { prvResetNextTaskUnblockTime(); //刷新下一个任务的阻塞时间 } taskEXIT_CRITICAL(); //程序退出临界段 } else { mtCOVERAGE_TEST_MARKER(); //用于代码覆盖率测试的宏,此处等价于一个空语句 } /* 如果挂起的是当前任务,进行任务切换处理 */ if( pxTCB == pxCurrentTCB ) { /* 任务调度器处于运行状态,触发 PendSV 异常,执行上下文切换到其他就绪任务 */ if( xSchedulerRunning != pdFALSE ) { /* The current task has just been suspended. */ configASSERT( uxSchedulerSuspended == 0 ); portYIELD_WITHIN_API(); } else //调度器未运行 { /* The scheduler is not running, but the task that was pointed * to by pxCurrentTCB has just been suspended and pxCurrentTCB * must be adjusted to point to a different task. */ /* 如果挂起任务列表的长度等于系统中任务总数,说明所有任务都已被挂起,没有就绪任务了。 * 这时设置 pxCurrentTCB = NULL,表示当前没有可运行的任务。等到将来有新任务创建或某个任务被恢复时,调度器会再次指定当前任务 */ if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ { /* No other tasks are ready, so set pxCurrentTCB back to * NULL so when the next task is created pxCurrentTCB will * be set to point to it no matter what its relative priority * is. */ pxCurrentTCB = NULL; } /* 否则:系统中还有其他任务不在挂起列表中(即仍处于就绪或阻塞态),调用 vTaskSwitchContext() * 选择一个新的最高优先级任务作为 pxCurrentTCB。这发生在调度器未启动时,所以直接进行软件切换而不是 pendSV。 */ else { vTaskSwitchContext(); } } } else { mtCOVERAGE_TEST_MARKER(); //用于代码覆盖率测试的宏,此处等价于一个空语句 } }
2.任务恢复
-
功能:将一个先前被挂起的任务恢复为就绪状态,使其重新参与调度。
-
参数 :
xTaskToResume为要恢复的任务句柄。 -
关键约束 :不能 是
NULL,也不能是当前正在执行的任务(因为当前任务不可能是挂起状态)void vTaskResume( TaskHandle_t xTaskToResume )
{
TCB_t * const pxTCB = xTaskToResume;/* It does not make sense to resume the calling task. */ /* 断言确保参数不为 NULL */ configASSERT( xTaskToResume ); /* The parameter cannot be NULL as it is impossible to resume the * currently executing task. */ /* 只有在恢复非当前任务且非空时才执行实际操作 */ if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) { taskENTER_CRITICAL(); //程序进入临界段 { /* 只有确实被挂起的任务才需要恢复。如果任务处于就绪、阻塞、运行或删除状态,这个检查会失败,直接跳过 */ if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) { traceTASK_RESUME( pxTCB ); //调试宏,需要用户自己实现 /* The ready list can be accessed even if the scheduler is * suspended because this is inside a critical section. */ /* 将任务的状态列表项从 xSuspendedTaskList 中摘除 */ ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); /* /* 将任务加入就绪队列:尾插到对应优先级链表,并置位就绪位图 */ */ prvAddTaskToReadyList( pxTCB ); /* A higher priority task may have just been resumed. */ /* 如果刚恢复的任务优先级大于或等于当前任务的优先级,就需要进行一次调度判断 */ if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) { /* This yield may not cause the task just resumed to run, * but will leave the lists in the correct state for the * next yield. */ /* 在抢占式调度(configUSE_PREEMPTION == 1)时,等于 portYIELD(),立即触发 PendSV 进行上下文切换。 * 在合作式调度时,是空宏,不强制切换,等待当前任务主动让出 CPU。 */ taskYIELD_IF_USING_PREEMPTION(); } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试,实际为空语句 } } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试,实际为空语句 } } taskEXIT_CRITICAL(); //程序退出临界段 } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试,实际为空语句 } }
3.就绪位图
3.1 定义
就绪位图是 FreeRTOS 多优先级调度的"索引":
- 一个整数(通常 UBaseType_t,32 位),每一位代表一个优先级是否有任务就绪。
- bit0 → 优先级 0,bit1 → 优先级 1,...,bit31 → 优先级 31。
- 如果某优先级有就绪任务,对应位置 1;该优先级就绪列表为空,则对应位清 0
3.2 核心作用
调度器利用硬件前导零计数指令(__clz)在 O(1) 时间内找到最高就绪优先级,从而快速选出下一个运行任务,延迟恒定
3.3 如果优先级数量超过32个怎么办?
你的定义基于 32 位整数 ,隐含了 configMAX_PRIORITIES ≤ 32。
如果配置的优先级数超过 32,FreeRTOS 会自动使用多个 32 位字组成就绪位图数组,例如:
#define portPRIORITY_GROUPS ( ( configMAX_PRIORITIES + 31 ) / 32 )
static UBaseType_t uxReadyPriorities[ portPRIORITY_GROUPS ];
4.声明
(1)Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
(2)文中代码来自FreeRTOS,遵循MIT许可证,许可证可参考:https://opensource.org/licenses/MIT
/*
* FreeRTOS Kernel V10.5.1
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/