2. 任务调度优化:优先级调度策略

好,咱们进入第二个核心话题——任务调度。说实话,调度策略选对了,电梯控制系统就成功了一半。我见过太多项目,硬件选型没问题,代码写得也漂亮,结果一跑起来就卡顿,最后发现是调度策略没选对。

电梯控制系统里,任务五花八门。有门控任务、速度曲线计算、楼层检测、通讯处理、故障监控……每个任务对时间的要求都不一样。怎么让它们井井有条地跑?这就是调度策略要解决的问题。

2.1 固定优先级调度

这是最直观的策略。每个任务分配一个固定的优先级,高优先级的任务先跑。说白了,就是「官大一级压死人」。

我在一个老项目里用过这种策略。当时电梯主控芯片是STM32F4,跑FreeRTOS。我把故障监控任务的优先级设得最高,因为安全第一。门控任务次之,通讯任务最低。跑起来很稳定,没出过问题。

但固定优先级有个坑——低优先级任务可能永远得不到CPU。你想想看,如果高优先级任务一直就绪,低优先级任务就饿死了。电梯里如果通讯任务一直抢不到CPU,那楼层显示就卡住了,乘客会投诉的。

关键点:固定优先级适合任务数量少、优先级层次分明的场景。电梯控制里,安全类任务用高优先级,非实时任务用低优先级。

2.2 EDF调度(最早截止时间优先)

EDF是个好东西。它不看优先级,看截止时间。哪个任务快到期了,就先跑哪个。动态的,很灵活。

我记得有一次做高速电梯项目,速度曲线计算任务必须在5ms内完成,否则电梯会抖动。用固定优先级的话,万一有个高优先级任务占用了CPU,速度曲线就超时了。换成EDF后,系统自动把快到期的任务优先处理,问题就解决了。

EDF的优点是CPU利用率高,理论上可以达到100%。但缺点也很明显——实现复杂,而且一旦过载,所有任务都可能错过截止时间。嗯,这就像高速公路堵车,所有车都走不动。

调度策略 优点 缺点 适用场景
固定优先级 实现简单,可预测性强 低优先级任务可能饿死 任务少、优先级明确
EDF CPU利用率高,动态适应 过载时所有任务都可能失败 实时性要求高、任务动态变化
RMS 理论成熟,可分析性强 对周期任务依赖大 周期任务为主的系统

2.3 RMS调度(速率单调调度)

RMS是固定优先级的一种特例。它的规则很简单:周期越短的任务,优先级越高。为什么?因为周期短的任务更紧急,你想想看,1ms跑一次的任务和100ms跑一次的任务,哪个更怕被耽误?

RMS有个著名的可调度性判定公式:

U = Σ(Ci/Ti) ≤ n(2^(1/n) - 1)

其中:
U = CPU利用率
Ci = 任务i的最坏执行时间
Ti = 任务i的周期
n = 任务数量

当n很大时,这个上限趋近于ln2 ≈ 69.3%。也就是说,用RMS调度,CPU利用率超过69.3%就可能出问题。我刚开始做电梯项目时,总想把CPU跑满,结果经常出问题。后来老老实实留了30%的余量,世界就清净了。

我的经验:RMS适合周期任务为主的系统。电梯控制里,速度曲线计算、楼层检测这些周期任务,用RMS分配优先级很合适。但非周期任务(比如故障中断)就得单独处理。

2.4 任务优先级分配原则

优先级怎么分?我总结了几条原则,都是血泪教训换来的:

  1. 安全第一:故障检测、急停处理这些任务,优先级必须最高。人命关天的事,不能含糊。
  2. 实时性要求高的优先:速度曲线计算、门控信号这些,错过时间就会出问题。
  3. 周期短的优先:参考RMS的思路,周期短的任务更紧急。
  4. 关键路径上的任务优先:比如电梯的启动、停止、换速这些关键动作,优先级要高。
  5. 非实时任务放最低:日志记录、数据显示这些,优先级最低,有空再跑。

我曾经在一个项目里,把通讯任务的优先级设得比门控任务还高。结果电梯到了楼层,门半天打不开,因为通讯任务抢了CPU。乘客投诉说电梯反应慢,后来我赶紧调了优先级,问题就解决了。

注意:优先级不是越多越好。一般RTOS支持256级优先级,但实际用10级以内就够了。优先级太多,反而容易搞混,而且调度开销也大。

2.5 避免优先级反转

优先级反转是个经典问题。简单说就是:高优先级任务等着低优先级任务释放资源,但低优先级任务被中等优先级任务抢占了CPU,结果高优先级任务反而被中等优先级任务「卡住」了。

我遇到过真实案例。电梯的故障检测任务(高优先级)和门控任务(低优先级)共享一个I2C总线。故障检测任务要读传感器数据,但门控任务正占着总线。这时候来了个通讯任务(中等优先级),把门控任务抢占了。结果故障检测任务干等着,电梯出了故障都检测不到。

解决方案有三种:

  • 优先级继承:低优先级任务在持有资源时,临时提升到高优先级任务的优先级。这样中等优先级任务就抢不了它了。
  • 优先级天花板:访问共享资源的任务,优先级直接提到资源对应的最高优先级。
  • 关中断:访问共享资源时关中断,简单粗暴,但会影响实时性。

我个人推荐优先级继承。FreeRTOS和uC/OS都支持这个机制。我那个I2C总线的案例,开了优先级继承后,问题就解决了。

避坑指南:我曾经在项目里忘了开优先级继承,结果电梯偶尔会「死机」几秒钟。排查了两天才找到原因。所以,只要涉及共享资源,一定要检查优先级反转的问题。

好了,任务调度这块就聊到这儿。记住一句话:调度策略没有银弹,要根据实际场景选。电梯控制系统里,我一般用固定优先级为主,配合优先级继承机制。如果实时性要求特别高,可以考虑EDF。RMS适合做理论分析,实际项目中用得不多。