1.任务挂起1.1 流程图获取 TCB 与移除工作进入临界区根据句柄获得任务控制块。将该任务从就绪/延迟列表移除若移除后该优先级链表为空则清除就绪位图对应位同时将其从事件等待列表移除若正在等待事件避免遗留无效等待项。加入挂起列表将任务的状态列表项插入xSuspendedTaskList末尾。若启用任务通知遍历其所有通知状态将处于等待通知的状态改为不再等待。退出临界区后处理若调度器运行中重新计算下一次任务唤醒时间以防原唤醒时间引用正是刚挂起的任务。自身挂起时的切换若挂起的是当前任务且调度器运行则断言调度器未挂起触发portYIELD_WITHIN_API进行上下文切换。若调度器未运行则检查是否所有任务都进入挂起状态挂起列表长度等于任务总数若是则pxCurrentTCB NULL否则调用vTaskSwitchContext选择新当前任务。1.2 源码解析功能将指定任务挂起。被挂起的任务不再参与调度直到被其他任务调用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.任务恢复2.1 流程图参数校验断言参数非空并确保不是当前任务当前任务不可能被挂起后由自身恢复。挂起状态检查进入临界区后调用prvTaskIsTaskSuspended确认任务确实处于挂起态防止误操作。恢复操作将任务的状态列表项从xSuspendedTaskList移除并调用prvAddTaskToReadyList将其放入对应优先级的就绪链表。调度判断若恢复的任务优先级高于或等于当前任务调用taskYIELD_IF_USING_PREEMPTION抢占式调度下会立即请求上下文切换合作式调度下则忽略。退出临界区恢复完毕。2.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_t32 位每一位代表一个优先级是否有任务就绪。bit0 → 优先级 0bit1 → 优先级 1…bit31 → 优先级 31。如果某优先级有就绪任务对应位置 1该优先级就绪列表为空则对应位清 03.2 核心作用调度器利用硬件前导零计数指令__clz在 O(1) 时间内找到最高就绪优先级从而快速选出下一个运行任务延迟恒定3.3 如果优先级数量超过32个怎么办你的定义基于32 位整数隐含了configMAX_PRIORITIES ≤ 32。如果配置的优先级数超过 32FreeRTOS 会自动使用多个 32 位字组成就绪位图数组例如#define portPRIORITY_GROUPS ( ( configMAX_PRIORITIES 31 ) / 32 ) static UBaseType_t uxReadyPriorities[ portPRIORITY_GROUPS ];4.声明1Copyright (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 * */【以上内容为个人在学习FreeRTOS过程中的源码解读笔记欢迎大家在评论区讨论指正。】【如果本篇内容对你有帮助不妨点个关注你的支持是我持续更新的动力】