3、实时调度策略概览:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE、SCHED_OTHER与SCHED_BATCH对比
嵌入式Linux内核提供了多种调度策略,以满足不同工作负载对实时性、公平性和吞吐量的需求。本章将系统对比五种核心调度策略:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE、SCHED_OTHER 和 SCHED_BATCH,帮助您在实际项目中做出正确的策略选择。
3.1 调度策略分类与适用场景
Linux调度器将进程分为两大类:实时进程(使用SCHED_FIFO、SCHED_RR、SCHED_DEADLINE)和普通进程(使用SCHED_OTHER、SCHED_BATCH、SCHED_IDLE)。实时进程拥有更高的优先级,确保关键任务获得确定的CPU时间。
3.2 五种调度策略核心对比
下表从优先级范围、调度规则、时间片、适用场景等维度进行横向对比:
| 策略 | 优先级范围 | 调度规则 | 时间片 | 典型场景 |
|---|---|---|---|---|
| SCHED_FIFO | 1~99(实时) | 先入先出,运行至阻塞或主动让出 | 无(无限) | 中断处理、高优先级控制循环 |
| SCHED_RR | 1~99(实时) | 轮转调度,时间片耗尽后同优先级轮换 | 默认100ms(可调) | 多路实时数据采集、软实时多媒体 |
| SCHED_DEADLINE | 0(内部映射) | 基于Earliest Deadline First(EDF) | 由Runtime/Deadline/Period参数决定 | 硬实时控制、自动驾驶感知融合 |
| SCHED_OTHER | 0~39(nice值) | 完全公平调度(CFS),按权重分配 | 动态计算 | 普通用户进程、Shell交互 |
| SCHED_BATCH | 0~39(nice值) | 类似CFS,但偏向批处理,减少唤醒抢占 | 动态计算(较长) | 后台编译、数据转码、日志处理 |
3.3 调度策略层次结构图
下图展示了Linux内核中调度策略的层次关系与优先级映射:
3.4 各策略详细解析
3.4.1 SCHED_FIFO(先入先出)
SCHED_FIFO是最简单的实时调度策略。一旦获得CPU,任务会一直运行直到:主动调用sched_yield()、被更高优先级任务抢占、或进入阻塞状态(如等待I/O)。同优先级任务按就绪队列顺序执行。
// 设置SCHED_FIFO策略,优先级50
struct sched_param param;
param.sched_priority = 50;
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
perror("sched_setscheduler failed");
}
3.4.2 SCHED_RR(轮转调度)
SCHED_RR在SCHED_FIFO基础上增加了时间片轮转机制。同优先级的多个RR任务会轮流获得CPU时间片(默认100ms),避免单个任务独占CPU。时间片耗尽后,任务被放到同优先级队列尾部。
// 设置SCHED_RR策略,优先级80,时间片100ms
struct sched_param param;
param.sched_priority = 80;
if (sched_setscheduler(0, SCHED_RR, ¶m) == -1) {
perror("sched_setscheduler failed");
}
// 查看当前时间片长度(单位:ns)
int time_slice = sched_rr_get_interval(0, NULL);
printf("RR time slice: %d ns\n", time_slice);
3.4.3 SCHED_DEADLINE(截止时间调度)
SCHED_DEADLINE基于EDF(Earliest Deadline First)算法,是Linux 3.14引入的硬实时调度策略。任务需指定三个参数:Runtime(每次执行所需CPU时间)、Deadline(相对截止时间)、Period(任务周期)。调度器保证在截止时间前完成执行。
// 设置SCHED_DEADLINE参数
// Runtime=10ms, Deadline=30ms, Period=30ms
struct sched_attr attr;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.sched_policy = SCHED_DEADLINE;
attr.sched_runtime = 10 * 1000 * 1000; // 10ms (ns)
attr.sched_deadline = 30 * 1000 * 1000; // 30ms (ns)
attr.sched_period = 30 * 1000 * 1000; // 30ms (ns)
if (sched_setattr(0, &attr, 0) == -1) {
perror("sched_setattr failed");
}
Runtime ≤ Deadline ≤ Period,且CPU利用率 = Runtime / Period ≤ 1(单核)。否则调度器可能无法保证所有任务满足截止时间。
3.4.4 SCHED_OTHER(完全公平调度)
SCHED_OTHER是Linux默认调度策略,基于CFS(Completely Fair Scheduler)算法。CFS维护一个红黑树,按虚拟运行时间(vruntime)排序,每次选择vruntime最小的任务运行。nice值用于调整任务权重,nice值越低,获得的CPU时间越多。
// 设置SCHED_OTHER策略,nice值-5(较高优先级)
struct sched_param param;
param.sched_priority = 0; // SCHED_OTHER必须为0
if (sched_setscheduler(0, SCHED_OTHER, ¶m) == -1) {
perror("sched_setscheduler failed");
}
// 调整nice值
if (setpriority(PRIO_PROCESS, 0, -5) == -1) {
perror("setpriority failed");
}
3.4.5 SCHED_BATCH(批处理调度)
SCHED_BATCH专为CPU密集型批处理任务设计。它与SCHED_OTHER共享CFS基础,但调度器会减少唤醒抢占行为,让批处理任务运行更长时间,降低上下文切换开销。SCHED_BATCH任务在唤醒时不会立即抢占当前任务,而是等待当前任务时间片用完。
// 设置SCHED_BATCH策略
struct sched_param param;
param.sched_priority = 0;
if (sched_setscheduler(0, SCHED_BATCH, ¶m) == -1) {
perror("sched_setscheduler failed");
}
3.5 调度策略选择决策流程
以下SVG流程图帮助您根据任务特性快速选择调度策略:
3.6 调度策略切换与优先级继承
在实际嵌入式系统中,任务可能需要在不同调度策略间切换。例如,一个普通任务在进入关键代码段时临时提升为实时优先级。Linux提供了sched_setscheduler()系统调用实现动态切换。
- 使用 优先级继承协议(PI Futex)
- 使用 优先级天花板协议
- 避免实时任务与非实时任务共享锁
// 动态切换策略示例:从SCHED_OTHER切换到SCHED_FIFO
struct sched_param param;
param.sched_priority = 60;
// 进入临界区前提升优先级
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
perror("Failed to enter real-time mode");
}
// ... 执行关键操作 ...
// 退出临界区后恢复普通调度
param.sched_priority = 0;
if (sched_setscheduler(0, SCHED_OTHER, ¶m) == -1) {
perror("Failed to leave real-time mode");
}
3.7 性能指标对比总结
| 指标 | SCHED_FIFO | SCHED_RR | SCHED_DEADLINE | SCHED_OTHER | SCHED_BATCH |
|---|---|---|---|---|---|
| 最坏情况延迟 | 极低(μs级) | 低(受时间片影响) | 可预测(满足截止时间) | 高(ms级) | 高(ms级) |
| 调度开销 | 低 | 低 | 中等(EDF计算) | 中等(CFS红黑树) | 低(减少抢占) |
| 公平性 | 无(按优先级) | 同优先级公平 | 按截止时间 | 完全公平 | 偏向批处理 |
| 适用CPU核数 | 单核/多核 | 单核/多核 | 单核/多核 | 单核/多核 | 单核/多核 |
| 配置复杂度 | 低(仅优先级) | 低(优先级+时间片) | 高(三个时间参数) | 低(nice值) | 低(nice值) |
3.8 实践建议
- 硬实时任务(如电机FOC控制、激光雷达数据采集):优先使用SCHED_DEADLINE,确保截止时间可预测。
- 软实时周期性任务(如音频编码、传感器融合):使用SCHED_FIFO或SCHED_RR,优先级根据周期和重要性分配。
- 非实时后台任务(如日志写入、固件升级):使用SCHED_BATCH,减少对实时任务的干扰。
- 交互式任务(如命令行、监控UI):使用SCHED_OTHER,配合nice值调整响应速度。
- 避免混合使用:实时任务和非实时任务应尽量隔离到不同CPU核,或使用cgroup进行资源限制。
chrt -p <pid> 查看进程当前调度策略和优先级;使用 chrt -f 50 ./my_realtime_app 以SCHED_FIFO优先级50启动程序。