news 2026/5/8 16:53:19

FreeRTOS源码解析(3)任务挂起与恢复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FreeRTOS源码解析(3)任务挂起与恢复

1.任务挂起

1.1 流程图

  1. 获取 TCB 与移除工作:进入临界区,根据句柄获得任务控制块。将该任务从就绪/延迟列表移除,若移除后该优先级链表为空则清除就绪位图对应位;同时将其从事件等待列表移除(若正在等待事件),避免遗留无效等待项。

  2. 加入挂起列表:将任务的状态列表项插入xSuspendedTaskList末尾。若启用任务通知,遍历其所有通知状态,将处于等待通知的状态改为不再等待。

  3. 退出临界区后处理:若调度器运行中,重新计算下一次任务唤醒时间,以防原唤醒时间引用正是刚挂起的任务。

  4. 自身挂起时的切换

    • 若挂起的是当前任务且调度器运行,则断言调度器未挂起,触发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 流程图

  1. 参数校验:断言参数非空,并确保不是当前任务(当前任务不可能被挂起后由自身恢复)。

  2. 挂起状态检查:进入临界区后,调用prvTaskIsTaskSuspended确认任务确实处于挂起态,防止误操作。

  3. 恢复操作:将任务的状态列表项从xSuspendedTaskList移除,并调用prvAddTaskToReadyList将其放入对应优先级的就绪链表。

  4. 调度判断:若恢复的任务优先级高于或等于当前任务,调用taskYIELD_IF_USING_PREEMPTION;抢占式调度下会立即请求上下文切换,合作式调度下则忽略。

  5. 退出临界区:恢复完毕。

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_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 * */

【以上内容为个人在学习FreeRTOS过程中的源码解读笔记,欢迎大家在评论区讨论指正。】
【如果本篇内容对你有帮助,不妨点个关注,你的支持是我持续更新的动力!】

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 16:51:53

2026工业消泡剂选型:合规厂家挑选方法全解析

如果你是山东、华北地区的污水处理厂运营主管、造纸厂生产经理、印染厂环保专员、工业循环水处理服务商等工业领域从业者&#xff0c;近期正在寻找适配自身工况的消泡剂产品与合规供货厂家&#xff0c;可参考以下整理的全流程选型逻辑&#xff0c;避开选购误区&#xff0c;找到…

作者头像 李华
网站建设 2026/5/8 16:51:49

Best Hospital Information System(HIS)文档处理对比与优化

在医疗信息化领域&#xff0c;评选“最佳医院信息系统&#xff08;HIS&#xff09;”始终是一个多维度的复杂命题。传统评估往往聚焦于功能模块数量、响应速度或用户界面友好度。据研究&#xff0c;随着跨机构协同、医疗举证合规和电子文档司法认可要求的提升&#xff0c;文档处…

作者头像 李华
网站建设 2026/5/8 16:50:54

FPGA智能调试工具:从海森堡Bug到实时洞察的验证革命

1. 项目概述与核心痛点 作为一名在FPGA和SoC设计一线摸爬滚打了十几年的工程师&#xff0c;我太清楚项目后期验证和调试阶段那种“黎明前的黑暗”是什么滋味了。你花了几个月精心设计RTL&#xff0c;跑通了仿真&#xff0c;满怀信心地把设计烧录进板子&#xff0c;结果系统一上…

作者头像 李华
网站建设 2026/5/8 16:50:40

全球电动汽车转型:2035年关键节点与产业链重塑

1. 全球电动汽车转型浪潮&#xff1a;数据背后的产业逻辑 如果你最近关注汽车新闻&#xff0c;大概率会看到“某国宣布2035年禁售燃油车”的标题。这并非孤立事件&#xff0c;而是一场席卷全球的、有明确时间表的产业革命。根据行业分析师Egil Juliussen在2022年汇总的数据&…

作者头像 李华
网站建设 2026/5/8 16:48:39

技术演进:从单体到模块化的AI图像处理架构革命

技术演进&#xff1a;从单体到模块化的AI图像处理架构革命 【免费下载链接】ComfyUI-Impact-Pack Custom nodes pack for ComfyUI This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more. 项目地址: https://gitcode…

作者头像 李华
网站建设 2026/5/8 16:48:37

什么是AIGC检测?AIGC检测原理是什么?如何降AI率?

现在&#xff0c;人工智能&#xff08;AI&#xff09;写作工具已经非常普及&#xff0c;还有谁没用 AI 写过邮件、报告&#xff0c;甚至论文&#xff1f;但随之而来的一个新问题也浮出水面&#xff1a;如何判断一段文字是人写的&#xff0c;还是 AI 生成的&#xff1f;这就是 A…

作者头像 李华