ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

第317篇 实时调度策略详解

2026/9/1 15:14:09 拓冰建站 浏览量
第317篇 实时调度策略详解 前面两篇聊了实时系统基础和PREEMPT_RT。这篇聚焦到一个核心话题调度策略。在实时系统中调度策略决定了哪个任务在什么时候获得CPU——这是实时性能的根基。Linux中有几种不同的调度策略适用于不同的场景。理解它们的区别和适用场景对机器人软件开发非常重要。Linux的调度策略Linux支持以下几种调度策略。SCHED_OTHER也叫SCHED_NORMAL普通进程的默认策略。使用CFSCompletely Fair Scheduler调度器按权重分配CPU时间。nice值影响权重——nice值越低负数权重越高获得的CPU时间越多。但不保证任何时间约束。SCHED_FIFO实时调度策略先到先服务。最高优先级的就绪任务一直运行直到它主动让出CPU调用sched_yield或阻塞或者被更高优先级的任务抢占。没有时间片——同优先级的任务不会轮转。适合需要连续执行的实时任务。SCHED_RR实时调度策略时间片轮转。跟SCHED_FIFO类似但同优先级的任务之间有时间片。每个任务运行一个时间片后让出CPU下一个同优先级的任务运行。适合多个同优先级实时任务需要公平分享CPU的场景。SCHED_DEADLINEEDFEarliest Deadline First调度策略。每个任务有一个截止时间和运行时间参数调度器选择截止时间最近的任务优先执行。这是理论上最优的实时调度算法但使用相对复杂。// 设置SCHED_FIFO调度策略 struct sched_param param; param.sched_priority 50; // 优先级1-99 sched_setscheduler(pid, SCHED_FIFO, param); // 设置SCHED_DEADLINE需要Linux 3.14 struct sched_attr attr; attr.size sizeof(attr); attr.sched_policy SCHED_DEADLINE; attr.sched_runtime 500000; // 运行时间500us attr.sched_deadline 1000000; // 截止时间1ms attr.sched_period 1000000; // 周期1ms sched_setattr(pid, attr, 0);优先级分配的原则实时优先级的范围是1到9999最高。怎么分配优先级是个需要仔细考虑的问题。最高优先级90-99留给最关键的硬实时任务。比如电机保护——检测到过流时必须在几微秒内切断输出。这些任务几乎不运行但一旦运行就必须立即执行。高优先级70-89控制循环。比如1kHz的PID控制、状态估计。这些任务周期性运行对延迟敏感。中优先级50-69传感器数据处理。比如IMU数据滤波、编码器计数。这些任务也很重要但可以容忍稍大的延迟。低优先级1-49通信处理、日志记录等非实时任务。普通进程SCHED_OTHER的优先级低于所有实时进程。所以即使系统负载很高实时任务也能立即获得CPU。优先级分配还要注意避免优先级饥饿。如果你把所有实时任务都设为最高优先级99那它们之间就没有区分度了——先来后到完全取决于调度时机不可控。合理分配优先级让系统行为更可预测。还有一个实践技巧在系统启动脚本中统一设置所有实时任务的优先级。不要在每个程序内部自己设置——那样优先级分配分散在各处很难管理和审计。用一个配置文件比如/etc/realtime.conf集中管理启动时由一个脚本统一设置。SCHED_DEADLINE的使用场景。目前SCHED_DEADLINE在机器人中用得不多主要原因是配置复杂、对阻塞操作的支持不完善、调试工具少。但它的理论基础最好——EDF在单处理器上是最优的调度算法。随着内核支持的完善SCHED_DEADLINE可能会成为实时调度的主流选择。SCHED_DEADLINE的一个限制是任务不能超出声明的runtime。如果任务执行时间超过了runtime内核会发送SIGXCPU信号默认行为是终止进程。这对调试来说是个挑战——你需要确保在所有可能的情况下最坏的输入、最多的中断任务的执行时间都不超过runtime。WCET分析在这里变得非常重要。SCHED_DEADLINE的优势SCHED_DEADLINE是Linux 3.14引入的调度策略基于EDF算法。每个任务声明三个参数运行时间runtime、相对截止时间deadline、周期period。调度器保证每个任务在截止时间前完成。SCHED_DEADLINE的优势是可以做可调度性分析。如果你知道每个任务的runtime、deadline和period可以用EDF的可调度性条件来判断这组任务是否可行Σ(runtime_i / period_i) ≤ 1。如果满足这个条件所有任务都能满足截止时间。在机器人中SCHED_DEADLINE特别适合周期性的控制任务。你可以给控制循环设置runtime500us, deadline1ms, period1ms调度器会保证控制循环在每毫秒的前500微秒内完成。但SCHED_DEADLINE的使用有一些限制。需要内核配置CONFIG_SCHED_DEADLINE。不是所有的阻塞操作都能正确处理——如果任务在运行中阻塞了比如等待Mutex调度器的保证可能失效。实际项目中用得还不多但前景很好。面试要点SCHED_FIFO和SCHED_RR的区别。SCHED_FIFO没有时间片同优先级任务不会轮转——先就绪的一直运行。SCHED_RR有时间片同优先级任务轮流执行。如果你只有一个实时任务用SCHED_FIFO如果有多个同优先级的实时任务需要公平分享CPU用SCHED_RR。优先级反转问题。高优先级任务等待低优先级任务持有的锁同时被中优先级任务间接阻塞。解决办法优先级继承协议Linux的rt_mutex支持或者优先级天花板协议。面试时能解释清楚优先级反转的场景和解决方案会加分。实时任务的设计原则。实时任务应该尽量短小——执行完就释放CPU。不要在实时任务中做IO操作文件读写、网络通信这些操作可能导致长时间阻塞。不要在实时任务中使用malloc——内存分配的时间不确定。实时任务的栈应该预先分配并锁定mlock。实时任务的模式通常是等待事件阻塞→处理事件快速计算→发送结果→等待下一个事件。处理阶段要尽可能快把耗时的操作比如数据记录、网络传输交给非实时的辅助线程。这种实时线程辅助线程的模式在机器人控制中非常常见。多核环境下的调度。在多核系统上实时任务通常绑定到特定的CPU核心用sched_setaffinity。这样可以避免任务在核心之间迁移带来的缓存失效开销。一般把控制任务绑定到一个隔离的核心上其他核心跑非实时任务。配合前面提到的CPU隔离isolcpus效果最好。给你的建议学习调度策略最好的方式是写几个测试程序分别用不同的策略运行用chrt观察效果。创建一个SCHED_FIFO优先级90的任务和一个SCHED_OTHER的任务同时运行一个CPU密集型程序观察SCHED_FIFO任务的响应时间。在机器人项目中控制循环一定要用实时调度策略。推荐用SCHED_FIFO优先级设在70-80之间。不要用SCHED_RR——控制循环通常只有一个不需要时间片轮转。面试时聊调度策略核心要展示的是你对实时调度的理解。不只是知道怎么设置优先级还要知道为什么这样设置、不同策略的trade-off、以及怎么分析和验证实时性。能举出一个你在实际项目中做调度优化的例子比如怎么通过调整优先级解决了延迟问题这比泛泛而谈更有说服力。补充一个实用建议用cgroupControl Groups来管理实时任务的CPU资源。cgroup v2支持cpuset控制器可以精确控制实时任务能使用哪些CPU核心。配合systemd的Slice功能可以把所有实时任务放在一个cgroup中统一管理。这种方式比单独设置每个任务的affinity更系统化适合有多个实时任务的复杂系统。上一篇第316篇 Linux PREEMPT_RT实时补丁下一篇预告第318篇 实时性能分析与优化