使用FreeRTOS,要好好理解任务状态机

嵌入式资讯精选 2022-05-06 11:29

前面一文利用FreeRTOS点灯,算是将FreeRTOS给跑起来了,要用好RTOS,从黑盒角度去理解一下调度器是怎么工作的是很必要的,当然如果想研究其内部实现原理,可以去读其内部实现代码,但是个人感觉如果是从用的角度,把内核看成黑盒,跳出来梳理一下概念也很有用。

所以本文不切入内核代码,仅从用户视角来学习一下任务状态机相关的概念,以及相应API的作用。

RTOS核的作用

前面一文分析FreeRTOS框架的时候,曾给出这样一个理解图:

对于单片机而言,一般只有一个核,RTOS的主要作用是将用户多任务进行管理,在物理CPU核上调度管理。所以为了方便理解,可以将RTOS的调度管理器,看成是将硬件CPU核通过软件的办法为每一个应用任务虚拟出一个软核。这样就使每个任务看起来都拥有一个CPU核,这样从时间维度上看起来多任务是并行的,而事实上这种并行是伪并行。

一般单片机只有一个硬件核,那么在任意时刻,则只可能有一个任务在运行。其实这样理解还不全面,能够获取CPU时间的,从应用编程的视角,还有一个主角是不能忽略的,那就是中断程序。

任务状态

状态概念

对于FreeRTOS的状态概念有必要先好好理解一下,理解了才能正确的使用API进行正确的应用,才知道调用了某一个API究竟会有怎样的行为表现。

<>在任务管理章节,首先给出任务的一个顶层状态机视图:

对于单内核的芯片而言,任一任务要么处于运行态,要么处于非运行态。但同一时刻只能有一个任务处于运行态。这也是为什么这个图中①画的任务框是多个叠起来的,而②所示的任务只有一个框的原因。那么事实上,对于非运行态其内部又被划分出了几个子状态:

  • Suspended: 挂起态,什么叫挂起呢?简单讲就是任务进入了挂起态后,调度器就不会对其进行调度了,也就是它不会被调度器装载到CPU核中运行,任务状态始终保持在进入挂起态时刻的现场。

    就好比看一个修仙剧,内核调度器是一个法术高手,会时间静止法术,啪一个法术,这个任务就被定住了,不能再动了。但任务还在,只是不动了。直到法术解除。那么这里所谓的现场,就是该任务的TCB任务控制数据结构,将暂停时刻的物理CPU相关寄存器保存了。

  • Ready: 就绪态,就是指任务可以被调度器装载进CPU核运行的状态,但是还没有被装载进CPU核。为什么有这样一个就绪态呢?前面说了,RTOS主要作用就是多任务的调度管理。那么就绪的任务就有可能是多个,也就是说在同一时刻,多个任务有可能都就绪了,至于调度器究竟让哪一个任务先运行呢,这就是调度器调度算法的职责了,根据其内部的调度算法策略进行调度管理。

    FreeRTOS支持的调度算法有:

    • 时间片调度策略:也称为Round Robin调度算法,Round Robin调度算法不保证同等优先级的任务之间平均分配时间,只保证同等优先级的Ready状态任务会依次进入Running状态。

      这可能让人费解,首先时间片Time Slice是指两个Tick中断间的时间间隔,每次新的Tick中断时,调度器会检查任务队列中是否有与正在运行的任务优先级相同的就绪态任务,如果有,就将正在运行的任务换出CPU,将新任务换入CPU。所以该机制并不保证相同优先级就绪态任务获得的CPU时间片相等。

    • 固定优先级抢占式调度:这种调度算法根据任务的优先级选择任务进行装载。换句话说,高优先级任务总是在低优先级任务之前获得CPU。只有当没有处于就绪状态的高优先级任务时,低优先级任务才能执行。

      更准确地理解:如果优先级高于运行状态任务的任务进入就绪状态,抢占式调度算法将立即“抢占”运行状态的低优先级任务。被抢占意味着低优先级任务马上被调度器换出运行状态,并进入就绪状态,而高优先级任务被转载进CPU进行运行。需要注意的是,低优先级任务是进入就绪态而非挂起态,当高优先级任务完成运行,进入阻塞态后,原低优先级任务将有机会被调度运行。

  • Blocked: 阻塞态。所谓阻塞态,可以简单理解是任务被卡在了哪里,该任务不会继续往下运行,直到阻塞解除,被转入就绪态,然后被调度至运行态。需要注意区分的是:阻塞态与暂停态是两回事,暂停是被移除调度列表,除非被人为恢复进任务调度表。而阻塞态,当阻塞事件解除,会自动进入就绪态,从而有机会被调度器换入CPU进而运行。

    阻塞事件基本可以分成两类:

    • 时间事件:比如vTaskDelay调用,任务将延迟一定的时间,一旦该函数被调用,该任务就被阻塞,直到延迟的时间结束会进入就绪态。
    • 同步事件:比如等待消息队列、获取信号量、获取互斥体等等。

上面说到抢占式调度算法,看下面这个图就比较好理解了,在图中所示的时间点,高优先级的任务一旦就绪则会马上抢占低优先级任务。

状态切换

前面将状态概念撸了一遍,状态机的理解需要从两个维度进行理解:1.有哪些状态,每个状态啥物理含义;2.状态的切换条件,什么条件会触发状态变化。

上面的任务状态图描述的比较清楚,这里总结一下这些状态究竟怎么切换的:

  • 进入挂起态:在任务的任意状态下,一旦应用程序调用了vTaskSuspend这个API,就会将指定的任务设置挂起态。
void vTaskSuspend( TaskHandle_t pxTaskToSuspend )
void vTaskSuspendAllvoid )

以上两个任务都可以用于将任务设置成挂起态,vTaskSuspend用于将指定的任务设置为挂起态,pxTaskToSuspend就是指定的任务描述符,而vTaskSuspendAll将所有任务设置成挂起态。

  • 退出挂起态:当任务已经处于挂起态,如应用需要将其恢复,需要调用vTaskResume或者xTaskResumeAll,将某个任务或者全部任务恢复为就绪态。注意是就绪态而非运行态,进入运行态是调度器实现的。
void vTaskResume( TaskHandle_t pxTaskToResume )
BaseType_t xTaskResumeAllvoid )

要让任务恢复运行,上面两个API必须要在非挂起态任务中调用,否则是不可能被恢复的,因为处于挂起态的任务是没有机会获得CPU使用权运行的。

对于挂起态的应用场景的思考,比如应用程序中检测到某个故障了,此时需要处理故障,就可以将某个任务挂起,或者全部挂起,直到故障消除。

  • 进入阻塞态:阻塞的概念是相对于运行而言的,也就是说一个正在运行的任务由于OS API调用会卡住不往下运行,所以状态图中是运行态会被阻塞,也就是说该任务本来正在运行,但在这个调用之后就会被调度器换出CPU。

有哪些API会让一个正处于运行的任务阻塞呢?

1.时间事件API:

void vTaskDelay( TickType_t xTicksToDelay )
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement );

这两个API是当任务希望主动出让CPU时使用,一旦调用该任务就被设置为阻塞态,直到需要等待的时间结束,调度器将相应的任务设置为就绪态。调度器再根据调度算法决定是否被装载进CPU核运行。

应用例子:比如某个需要固定周期执行的任务,就可以在任务应用代码执行完后调用这个延迟函数,出让CPU。让其他的任务有机会被转载运行。

vTaskDelayUntil一般会先获取当前Tick数,然后再延迟到某一个增加量。

2.同步事件API:

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
BaseType_t xTaskNotifyWaituint32_t ulBitsToClearOnEntry, 
                uint32_t ulBitsToClearOnExit, 
                uint32_t *pulNotificationValue, 
                TickType_t xTicksToWait )
;

//消息队列相关
BaseType_t xQueueReceive( QueueHandle_t xQueue,  
               void *pvBuffer,  
               TickType_t xTicksToWait )

BaseType_t xQueueReceiveFromISR(  QueueHandle_t xQueue,  
                   void *pvBuffer,  
                   BaseType_t *pxHigherPriorityTaskWoken )

BaseType_t xQueuePeek( QueueHandle_t xQueue,  
             void *pvBuffer, TickType_t  
             xTicksToWait )

BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void *pvBuffer )

//信号量相关
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait )
BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,  
                   signed BaseType_t *pxHigherPriorityTaskWoken )

BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,  
                    TickType_t xTicksToWait )


//stream相关
size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, 
                void *pvRxData, 
                size_t xBufferLengthBytes, 
                TickType_t xTicksToWait )

size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, 
                    void *pvRxData, 
                    size_t xBufferLengthBytes, 
                    BaseType_t *pxHigherPriorityTaskWoken )

//Event相关
EventBits_t xEventGroupWaitBitsconst EventGroupHandle_t xEventGroup, 
                  const EventBits_t uxBitsToWaitFor, 
                  const BaseType_t xClearOnExit, 
                  const BaseType_t xWaitForAllBits, 
                  TickType_t xTicksToWait )

EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, 
                const EventBits_t uxBitsToSet, 
                const EventBits_t uxBitsToWaitFor, 
                TickType_t xTicksToWait )


//message 相关
size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer, 
                 void *pvRxData, 
                 size_t xBufferLengthBytes, 
                 TickType_t xTicksToWait )

size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer, 
                    void *pvRxData, 
                    size_t xBufferLengthBytes, 
                    BaseType_t *pxHigherPriorityTaskWoken )

此类任务主要用于任务间,或者任务与中断间同步或通讯的目的,在等待某一个消息或者事件的时候,将该任务阻塞而不是裸奔的查询等待,本质上就是为了提高CPU的利用率的。

需要注意的是,有的API是不能用于等待来自中断的消息或者事件的,如果需要与中断程序同步或者通信,需要使用相应的中断版本API。

总结一下

将FreeRTOS任务相关的状态梳理一下,其他的RTOS其实也是类似的,只不过实现细节会略有差异,从概念上大体上是相通的。要正确的使用RTOS,清楚正确的理解其任务状态相关概念是必要的。相关的API并不需要记忆,只需要理解概念就可以了,用的时候查一查就好了。

1.中国芯片替代方案公司名单公布(70个类别)

2.为什么原理图总画不好?这些技巧要知道

3.嵌入式设备如何显示IP属地?

4.RISC-V面向MCU/MPU、RTOS,但面临挑战。。。

5.单片机固件自更新的一个方法!

6.玄铁杯RISC-V应用创新大赛正式开赛,现开放报名!

免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。


嵌入式资讯精选 掌握最鲜资讯,尽领行业新风
评论
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 140浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 134浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 114浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 178浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 99浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 185浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 122浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 170浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 54浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 77浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 65浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 104浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 87浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 94浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦