2. RTOS基础概念:任务、调度器、临界区、上下文切换、实时性指标

好,咱们正式开始啃RTOS的核心概念。说实话,很多初学者一上来就被这些术语吓住了。其实没那么玄乎,你把它想象成一个公司运作,就全明白了。

2.1 任务(Task)—— 干活的人

任务是什么?说白了,就是一段独立执行的代码。在裸机编程里,我们只有一个main函数在那死循环。但在RTOS里,我们可以创建多个任务,让它们看起来像“同时”在跑。

每个任务都有自己的栈空间、优先级和状态。我习惯把任务想象成公司里的员工:有的负责前台接待(低优先级),有的负责处理紧急订单(高优先级)。

任务的状态机:一个任务在生命周期里会经历这些状态:

  • 就绪(Ready):万事俱备,只等CPU
  • 运行(Running):正在占用CPU干活
  • 阻塞(Blocked):等某个事件,比如等延时、等信号量
  • 挂起(Suspended):被强制暂停

我在做灌溉控制器时遇到过一个问题:有个任务负责读取土壤湿度传感器,另一个任务负责控制电磁阀。如果湿度读取任务被阻塞了,阀门控制任务就得干等着。嗯,这里要注意——任务之间的依赖关系一定要理清楚。

3.2 调度器(Scheduler)—— 项目经理

调度器是RTOS的大脑。它决定“下一个该谁干活”。常见的调度策略有两种:

调度策略 特点 适用场景
抢占式调度 高优先级任务可以打断低优先级任务 实时性要求高的系统
时间片轮转 同优先级任务轮流使用CPU 多个同等重要的任务

我个人更偏爱抢占式调度。为什么?因为灌溉控制器里,水位报警任务必须第一时间响应,不能等阀门控制任务慢慢悠悠跑完。你想想看,如果水都漫出来了,还在那处理温度数据,那不就出大事了?

我的经验:在FreeRTOS里配置调度策略很简单,在FreeRTOSConfig.h里设置configUSE_PREEMPTION为1就行。但记住,优先级别设太多,一般8-16级就够了。我曾经见过有人设了32级,结果自己都搞不清哪个任务该跑哪个优先级。

2.3 临界区(Critical Section)—— 会议室

临界区这个概念,说白了就是“这段代码一次只能一个人进”。为什么需要它?因为多个任务可能会访问同一个全局变量或硬件寄存器。

举个例子:灌溉控制器里有个全局变量记录“今日总灌溉量”。任务A在加水,任务B在记录数据。如果两个任务同时修改这个变量,数据就乱了。

我曾经踩过的坑:有一次在临界区里调用了延时函数,结果整个系统卡死了。后来才明白——临界区里不能做阻塞操作!因为临界区通常会关中断,你延时的时候中断进不来,系统就死在那了。

保护临界区的常用方法:

  • 关中断:最简单粗暴,但会影响实时性
  • 使用互斥量(Mutex):更优雅,但要注意优先级反转
  • 使用信号量(Semaphore):适合资源计数场景

2.4 上下文切换(Context Switch)—— 换人干活

上下文切换,就是CPU从一个任务切换到另一个任务的过程。它要保存当前任务的“现场”(寄存器、栈指针等),再恢复下一个任务的“现场”。

这个过程是RTOS的核心开销。切换一次大概需要多少时间?嗯,在Cortex-M3上,FreeRTOS的切换时间大约在1-3微秒。听起来很快对吧?但如果你的系统里每秒切换几千次,这个开销就不可忽视了。

上下文切换的代价

  • 保存/恢复CPU寄存器
  • 更新任务控制块(TCB)
  • 执行调度算法
  • 切换栈指针

我建议:任务切换频率别太高,一般10-100ms切换一次就够了。灌溉控制这种慢速系统,100ms切换一次完全够用。

2.5 实时性指标—— 到底多快才算快?

实时性不是“快”,而是“可预测”。一个实时系统必须保证:在规定的截止时间之前完成任务。

衡量实时性的几个关键指标:

指标 说明 灌溉场景要求
响应时间 从事件发生到任务开始处理的时间 水位报警:<10ms
截止时间 任务必须完成的最晚时间 阀门控制:<50ms
抖动(Jitter) 响应时间的波动范围 传感器采样:<5ms
吞吐量 单位时间内处理的任务数 数据记录:>10条/秒

你可能会问:“这些指标怎么测?”我一般用逻辑分析仪抓GPIO引脚的电平变化。在任务入口和出口各翻转一次引脚,一看波形就全明白了。

避坑指南:我曾经在一个项目里,把所有的任务优先级都设成了相同值。结果系统响应时间忽快忽慢,查了两天才发现是时间片轮转导致的抖动。记住:关键任务一定要给高优先级,别偷懒。

好了,这一章的核心概念就这些。任务、调度器、临界区、上下文切换、实时性指标——这五个概念是RTOS的基石。你把这些搞明白了,后面移植和优化就顺风顺水了。