摘要: 本文聚焦于 FreeRTOS 操作系统,深入探讨任务状态及其转换逻辑,详细阐述基于链表的任务调度机制以及任务调度节奏,通过理论剖析与实例结合,旨在使读者全面理解 FreeRTOS 任务管理的核心原理,为高效嵌入式系统开发奠定坚实基础。
一、引言
在 FreeRTOS 这个精巧的嵌入式操作系统架构中,任务是系统运行的基本单元,而任务状态的有效管理以及合理的任务调度则是确保系统稳定、高效运行的关键所在。如同一场精心编排的舞台剧,每个任务都在不同的状态间流转,而任务调度机制则如同舞台导演,掌控着任务的出场顺序与表演时长,共同演绎出系统功能的精彩篇章。
二、任务状态
(一)就绪态(Ready)
就绪态的任务是指那些已经准备好运行,只等待 CPU 资源分配的任务。它们在任务就绪列表中排队等候,就像一群等待登上舞台表演的演员,随时准备一展身手。一旦 CPU 空闲且调度器决定分配资源给就绪态任务,这些任务就会进入运行态。例如,在一个多任务系统中,可能有多个任务负责不同的功能模块,如数据采集任务、数据处理任务和数据传输任务等。当数据采集任务完成一次采集并将数据存储好后,它可能就进入就绪态,等待 CPU 来执行后续的数据处理操作,只要 CPU 有空闲时间,它就有机会被调度执行。
(二)运行态(Runing)
运行态是任务正在 CPU 上执行的状态,此时任务拥有 CPU 的控制权,可以执行其任务代码中的指令。在任何给定时刻,只有一个任务能够处于运行态(在单核处理器系统中)。这个任务就像是舞台上正在表演的主角,独占着聚光灯和舞台资源。例如,当数据处理任务被调度进入运行态时,它会按照预定的算法对采集到的数据进行处理,如滤波、分析等操作,在其时间片内或者未被更高优先级任务抢占之前,它将持续占用 CPU 资源来完成这些操作。
(三)挂起态(Suspended)
挂起态是任务被暂停执行的一种状态,并且在没有外部干预的情况下不会自动恢复执行。任务进入挂起态通常是由于某些特定的系统操作或应用需求。例如,当系统需要对某个任务进行调试或者资源调整时,可以手动将该任务挂起。与阻塞态不同,挂起态的任务不会因为某个特定的事件或时间延迟而自动恢复就绪态,它需要其他任务或系统管理员显式地将其恢复为就绪态后才有可能再次被调度执行。比如,在一个复杂的嵌入式系统中,如果发现某个任务出现异常行为,可能会先将其挂起,以便进行故障排查和修复,在修复完成后再将其恢复到就绪态。
(四)阻塞态(Blocked)
阻塞态是任务由于等待某个特定事件或资源而暂停执行的状态。一个典型的例子就是在使用 vTaskDelay()
函数时,任务会进入阻塞态。当任务调用 vTaskDelay(100)
(这里假设 pdMS_TO_TICKS(100)
转换后为等待 10 个 Tick 周期),它会放弃 CPU 资源,进入阻塞态,系统会将其从就绪列表移除并放置到阻塞列表中。在这 10 个 Tick 周期内,任务不会参与 CPU 资源的竞争,直到延迟时间结束,任务才会从阻塞态转换回就绪态,重新进入就绪列表等待调度。例如,一个数据传输任务在发送完一批数据后,可能需要等待一段时间(如等待接收方的确认信号或者等待下一个传输周期),此时它就可以调用 vTaskDelay()
函数进入阻塞态,以避免浪费 CPU 资源。
三、任务调度:基于链表的管理机制
这些代码在freertos中的task.c中都有,大家可以看看源代码来加深理解
(一)任务就绪列表(链表)
FreeRTOS 利用链表来管理任务的就绪状态。任务就绪列表是一个关键的数据结构,它将所有处于就绪态的任务组织在一起。每个任务在创建时都会被分配一个任务控制块(TCB),其中包含了任务的各种信息,如任务优先级、任务状态等。当一个任务进入就绪态时,其对应的 TCB 会被插入到任务就绪列表的相应位置,这个位置通常是根据任务的优先级来确定的。例如,高优先级任务会被放置在就绪列表的头部,以便在调度时能够优先被选择。
图解一下:
PxCurrentTCB:指向这一次时间片运行的任务
那么在下一次调度的时候,流程是怎样的呢?
根据优先级,先从ReadList[55]开始搜索,这个就绪列表中没有任务,那么又往下一级进行搜索,知道ReadList[53],其中有三个任务了,上一次我执行的这个列表中的任务一,那么这次就会执行任务2,直到下一次调度又会执行任务3,雨露均沾,非常公平。
有些小伙伴可能会有疑问,按照这个调度流程,我难道不会一直调度任务1吗?
不会的,在就绪列表中,会有一个参数记录当前就绪列表上一次执行的是那一个任务,那么下一次来的时候,就会执行这个任务的下一个任务,就算上一个任务没有执行完他的时间片就被别的更高优先级的任务中断了,也会执行这个任务的下一个任务
(二)任务调度过程
任务调度器会定期查询任务就绪列表,以确定下一个要运行的任务。调度的基本流程如下:首先,调度器会从任务就绪列表的头部开始查找,因为头部通常是高优先级任务所在的位置。当找到一个就绪任务后,调度器会将当前运行任务(如果有)的上下文保存起来,然后将 CPU 控制权交给新选定的任务,使其进入运行态。新任务开始执行其任务代码,直到它主动放弃 CPU(如调用 vTaskDelay()
等函数进入阻塞态)、被更高优先级任务抢占或者其时间片用完(在时间片轮转调度策略下)。如果是时间片轮转调度且任务的时间片用完,该任务会被重新放置到就绪列表的适当位置(通常是根据优先级和时间片轮转规则),然后调度器再次从就绪列表中选择下一个任务进行执行。
四、任务调度的节奏
(一)根据链表来进行查询
任务调度的节奏在很大程度上是由系统的 Tick 中断驱动的。如前所述,系统以固定的 Tick 周期产生中断,每一次 Tick 中断发生时,系统会进行一系列的操作,其中包括更新任务的状态信息(如判断阻塞态任务是否到期可以转换为就绪态等)以及检查任务就绪列表。调度器会根据这些信息和任务就绪列表的情况来决定是否需要进行任务调度。例如,当一个阻塞态任务的延迟时间在某次 Tick 中断后到期,它会从阻塞列表中移除并插入到任务就绪列表中,然后调度器会在合适的时机(通常是下一次任务调度点)重新评估任务就绪列表,以确定是否要将这个刚刚变为就绪态的任务调度到运行态。这种基于链表查询和 Tick 中断驱动的任务调度节奏,确保了 FreeRTOS 能够在多任务环境下有条不紊地管理任务的执行顺序和资源分配,既能够及时响应高优先级任务的需求,又能够在一定程度上保证低优先级任务的公平执行机会,从而实现系统的高效稳定运行。
五、结论
FreeRTOS 的任务状态管理与调度机制是其核心功能的重要组成部分。通过对任务就绪态、运行态、挂起态和阻塞态的精细划分和有效管理,结合基于链表的任务调度机制以及由 Tick 中断驱动的任务调度节奏,FreeRTOS 能够在资源有限的嵌入式系统环境中,灵活地分配 CPU 资源,确保多个任务按照预定的规则和优先级有序地执行。深入理解这些机制对于开发高质量的嵌入式应用程序至关重要,开发人员可以根据系统的具体需求,合理设计任务结构、设置任务优先级以及运用任务调度策略,以实现系统性能的优化和功能的可靠实现。无论是简单的传感器数据采集与处理系统,还是复杂的工业自动化控制平台,FreeRTOS 的任务管理机制都为其提供了坚实的运行基础,助力嵌入式系统在各个领域发挥更大的作用。
最后,诚望诸位不吝赐下一键三连,以资鼓励与襄助,使知识之光辉得以更盛,创作之热忱得以长燃,于技术探索之途携手共进,共铸不凡!