3、调度策略基础:FIFO调度策略、Round Robin调度策略、Sporadic调度策略

调度策略,说白了就是操作系统决定「下一个该谁跑」的规则。QNX 作为硬实时系统,它的调度器可不是随便排队的。我做了这么多年嵌入式,见过太多因为调度策略选错导致系统崩溃的案例。今天咱们就把这三种策略掰开揉碎了讲清楚。

3.1 FIFO 调度策略(SCHED_FIFO)

FIFO,全称 First In First Out,先进先出。但别被名字骗了——它可不是简单的排队。

核心规则:

  • 同优先级线程按就绪顺序排队
  • 高优先级线程可以抢占低优先级线程
  • 线程主动让出 CPU 或阻塞时,才轮到下一个
  • 不会因为时间片耗尽而被切换

关键点: FIFO 策略下,一个线程只要不阻塞、不主动让出 CPU,它可以一直跑下去。这意味着什么?你想想看,如果这个线程里有个死循环,整个系统同优先级的其他线程就全废了。

我在项目中遇到过这样一个坑:有个同事用 FIFO 策略跑一个数据采集线程,结果采集函数里有个 while 循环等待硬件信号,硬件一直没响应,这个线程就把 CPU 占死了。其他同优先级的控制线程全饿死,系统直接失控。

避坑指南: 我曾经在调试一个雷达信号处理系统时,发现某个 FIFO 线程偶尔会卡死整个系统。查了三天,最后发现是某个库函数内部做了自旋等待。所以用 FIFO 策略时,一定要确保线程里没有长时间的自旋或死循环。

适用场景:

  • 需要最短响应时间的紧急任务
  • 任务执行时间短且可预测
  • 任务之间需要严格按顺序执行

3.2 Round Robin 调度策略(SCHED_RR)

Round Robin,时间片轮转。它和 FIFO 很像,但多了一个「时间片」的概念。

核心规则:

  • 同优先级线程按就绪顺序排队
  • 每个线程运行一个时间片后,自动切换到下一个
  • 高优先级线程可以抢占
  • 时间片长度可配置(默认 4 毫秒)

说白了,RR 就是给 FIFO 加了个「闹钟」。时间一到,不管线程跑没跑完,都得让位。这能防止某个线程霸占 CPU 太久。

我的习惯: 对于多个同优先级的周期性任务,我一般用 RR 策略。比如三个传感器数据采集任务,优先级相同,用 RR 让它们轮流跑,每个跑一个时间片,这样谁也不会饿死。

时间片设置经验:

场景 推荐时间片 说明
高频率控制任务 1-2 ms 响应快,但切换开销大
普通数据采集 4-8 ms 默认值,平衡性好
批量数据处理 10-20 ms 减少切换,提高吞吐

注意: 时间片设得太短,系统开销全花在线程切换上了。设得太长,又失去了 RR 的意义。我一般从 4ms 开始调,用 clock_gettime() 测量实际切换延迟。

3.3 Sporadic 调度策略(SCHED_SPORADIC)

Sporadic 策略,中文叫零星调度。这个策略比较特殊,它是 QNX 为偶发性任务专门设计的。

为什么需要它?

你想想看,有些任务平时不干活,但一旦触发就要立刻响应。比如紧急停机按钮、故障处理线程。如果用 FIFO 或 RR,这些任务得一直占着高优先级,浪费 CPU。用低优先级吧,响应又不够快。

核心机制:

  • 任务有一个「预算时间」和「 replenishment 周期」
  • 任务触发后,在预算时间内以高优先级运行
  • 预算用完后,降为低优先级
  • 经过一个周期后,预算自动恢复

说白了: Sporadic 策略给偶发任务一个「爆发期」。平时它是个低优先级的小透明,一旦有事,立刻变身高优先级大佬,干完活又缩回去。这样既保证了响应速度,又不浪费 CPU。

配置示例:

// 创建一个 Sporadic 线程
struct sched_param param;
param.sched_priority = 30;          // 高优先级
param.sched_ss_low_priority = 10;   // 低优先级
param.sched_ss_repl_period.tv_sec = 0;
param.sched_ss_repl_period.tv_nsec = 100000000; // 100ms 周期
param.sched_ss_init_budget.tv_sec = 0;
param.sched_ss_init_budget.tv_nsec = 10000000;  // 10ms 预算

pthread_setschedparam(thread, SCHED_SPORADIC, &param);

我在项目中用过 Sporadic 策略处理网络中断。平时网络空闲,线程在低优先级待着。一旦有大量数据包涌入,它立刻用高优先级处理,10ms 预算内处理完,然后自动降级。这样既不影响其他实时任务,又能应对突发流量。

经验之谈: 预算时间要留够。我曾经设了 5ms 预算,结果一次突发数据量太大,预算用完了还没处理完,线程被降级,导致数据堆积。后来改成 15ms,配合环形缓冲区,问题解决。

3.4 三种策略对比

特性 FIFO RR Sporadic
时间片 有(预算)
优先级变化 固定 固定 动态
CPU 占用 可能独占 公平分享 按需爆发
适用场景 紧急短任务 同优先级轮转 偶发紧急任务

嗯,这里要注意:这三种策略只影响同优先级线程之间的调度。不同优先级之间,永远是高优先级抢占低优先级,跟策略无关。

最后说一句,调度策略选型没有银弹。我一般遵循这个原则:紧急且短的任务用 FIFO,同优先级任务多且均衡用 RR,偶发紧急任务用 Sporadic。当然,具体还得看你的系统负载和硬件性能。多跑跑 topsloginfo,看看实际调度情况,比纸上谈兵靠谱得多。