3、任务划分与优先级设计:任务拆分原则、优先级分配策略(Rate Monotonic)、任务状态机(就绪/运行/阻塞/挂起)

好,我们进入第三章。这一章,说白了就是RTOS实战的「地基工程」。任务怎么拆?优先级怎么定?状态怎么管?这三件事要是没想清楚,后面写再多代码也是白搭。

我个人习惯,拿到一个项目需求,第一件事不是画电路图,也不是写驱动,而是先拿张白纸,把任务边界画出来。为什么?因为任务划分错了,后面改起来成本极高。我在一个温室项目中就吃过这个亏,当时把传感器采集和控制逻辑揉在一个任务里,结果调试时发现,温度采样一卡,整个控制链路都跟着抖。嗯,从那以后,我学会了「拆」。

3.1 任务拆分原则:别让一个任务干太多活

任务拆分,核心就一句话:高内聚、低耦合。每个任务只做一件事,并且把这件事做好。

具体怎么拆?我总结了三条原则,你写代码时可以直接套用:

  • 按功能边界拆分:传感器采集、数据滤波、控制算法、人机交互、通信上报……这些天然就是不同的功能模块。每个模块独立成一个任务,互不干扰。
  • 按时间尺度拆分:有些任务需要高频执行(比如电机PID控制,可能1ms一次),有些任务低频执行(比如温湿度上报,1秒一次就够了)。把不同时间尺度的逻辑拆开,避免高频任务被低频任务拖累。
  • 按资源依赖拆分:共享同一个外设(比如I2C总线、UART)的操作,尽量放在同一个任务里,或者用互斥量保护。否则,两个任务同时抢一个I2C,数据就乱了。

避坑指南:我曾经把一个「读取温湿度传感器+计算平均值+更新显示」全写在一个任务里。结果传感器偶尔I2C通信失败,任务卡在读取函数里,显示也不更新了,控制逻辑也停了。后来拆成三个任务:采集任务只管读,滤波任务只管算,显示任务只管刷。一个挂了,另外两个还能正常工作。

3.2 优先级分配策略:Rate Monotonic 调度

优先级怎么定?很多新手喜欢拍脑袋:「这个任务重要,给高优先级;那个任务不重要,给低优先级。」但这样很容易出问题——高优先级任务一直跑,低优先级任务永远得不到CPU。

在RTOS领域,有一个经典的优先级分配策略叫 Rate Monotonic Scheduling(RMS,单调速率调度)。它的核心思想很简单:任务的周期越短,优先级越高

为什么会这样?你想想看,周期短的任务意味着它需要更频繁地执行,如果它的优先级不够高,很容易被周期长的任务抢占,导致错过截止时间。RMS 从数学上证明了,只要任务集的总利用率不超过某个上限(单处理器上是约 69%),这种分配方式就能保证所有任务都在截止时间前完成。

举个例子,一个温室控制系统可能有这些任务:

任务 周期 执行时间 RMS优先级
电机PID控制 1 ms 0.2 ms 最高
传感器采集 10 ms 1 ms
数据上报 1000 ms 5 ms

按照RMS,周期最短的PID控制优先级最高,传感器采集次之,数据上报最低。这样,即使数据上报任务被频繁抢占,也不会影响电机控制的实时性。

个人经验:RMS 不是万能的。如果任务之间有明显的依赖关系(比如任务B必须等任务A的结果),单纯按周期分配优先级可能会出问题。我一般先用RMS定一个基线,然后根据实际依赖关系微调。另外,别忘了给「紧急但不周期」的中断处理留出空间——中断的优先级通常高于所有任务。

3.3 任务状态机:就绪/运行/阻塞/挂起

每个RTOS任务,本质上都是一个有限状态机。理解这四种状态,是理解RTOS行为的关键。

  • 就绪(Ready):任务已经准备好运行,只等CPU空闲。说白了,万事俱备,只欠东风。
  • 运行(Running):任务正在占用CPU执行代码。同一时刻,单核CPU上只有一个任务处于运行态。
  • 阻塞(Blocked):任务在等待某个事件(比如等待信号量、等待消息队列、调用延时函数)。此时任务不消耗CPU,事件到来后自动回到就绪态。
  • 挂起(Suspended):任务被主动暂停,通常由其他任务或中断调用挂起函数触发。挂起的任务不会自动恢复,必须由其他任务显式唤醒。

这四种状态之间的转换,我画个简图你感受一下:

创建 → 就绪 → 运行 → 阻塞 → 就绪
                ↓       ↑
              挂起 ← → 就绪

实际开发中,最常见的状态是「就绪」和「阻塞」之间的切换。比如一个传感器采集任务,大部分时间都在阻塞等待定时器触发,触发后进入就绪态,被调度器选中后进入运行态,采集完数据又回到阻塞态。

注意:我曾经遇到过一个bug,一个任务因为等待的信号量永远不来,一直处于阻塞态。但调试时看任务列表,发现它既不在就绪队列,也不在阻塞队列——原来是我忘了初始化信号量,任务直接进入了「挂起」态。嗯,从那以后,我每次创建任务和同步对象,都会在初始化函数里做一次完整的状态检查。

3.4 实战建议:如何把这些原则用起来

说了这么多理论,落地才是关键。我给你一个具体的操作步骤:

  1. 画功能框图:把系统所有功能列出来,按3.1的原则拆成独立任务。
  2. 标周期和执行时间:每个任务估算它的执行周期和最大执行时间(用逻辑分析仪或示波器实测最准)。
  3. 按RMS分配优先级:周期最短的给最高优先级,然后微调。
  4. 设计状态转换逻辑:明确每个任务在什么条件下进入阻塞、什么条件下被唤醒。
  5. 写一个简单的调度测试:让所有任务跑起来,观察是否有任务长时间得不到CPU(优先级反转),或者是否有任务频繁错过截止时间。

我个人习惯,在项目初期会先搭一个「骨架」——只包含任务创建、优先级分配和基本的状态切换,不写具体业务逻辑。跑通了再往里填代码。这样能尽早发现调度层面的问题,避免后期返工。

好,第三章就到这里。记住:任务划分是艺术,优先级分配是科学,状态管理是基本功。这三样练好了,RTOS项目就成功了一半。