Linux调度器通过就绪队列和虚拟运行时间(vruntime)决定下一个上CPU的进程,CFS调度器依据vruntime大小动态选择优先级最高的普通进程,而实时进程(如SCHED_FIFO)独立于CFS且永远抢占;renice仅调整CFS中普通进程的nice值,chrt则切换至实时调度类,二者互不干扰;调度时机由事件驱动(如中断、系统调用返回、进程唤醒等),非固定周期;SCHED_IDLE仅在系统空闲时运行,SCHED_BATCH适用于CPU密集型批处理以减少上下文切换。

调度器怎么决定“下一个该谁上CPU”
Linux调度器不是靠轮询或随机选进程,而是通过就绪队列 + 虚拟运行时间(vruntime)做决策。CFS(完全公平调度器)把每个普通进程的CPU使用历史折算成vruntime,值越小说明“欠得越少”,就越优先被挑中。它不分配固定时间片,而是动态计算——比如一个刚唤醒的I/O型进程vruntime会大幅下调,立刻获得高调度权重;而一个跑满CPU的编译进程,vruntime持续增长,自动让出机会。
实操中你不会直接读vruntime,但能通过/proc/PID/sched看到它的近似值(字段叫se.vruntime)。注意:这个值只在CFS类调度下有效,实时进程(SCHED_FIFO等)走另一套逻辑,vruntime字段为0且无意义。
为什么 chrt 和 renice 效果完全不同
renice 只影响普通进程的nice值(-20~19),它最终映射到内核static_prio(100~139),仅作用于CFS调度器;而chrt 设置的是sched_policy(如SCHED_RR)和rt_priority(1~99),直接切入实时调度类——这两类调度器互不干扰,且实时进程永远抢占CFS进程。
- 常见错误:用
renice -20试图让音视频程序“秒响应”,结果卡顿依旧 → 因为I/O延迟不在nice控制范围内,该用chrt -r 50切到实时类 - 危险操作:对非特权进程执行
chrt -f 99→ 触发Operation not permitted,需cap_sys_nice能力或root - 兼容性陷阱:容器环境(如Docker)默认禁用
SCHED_FIFO,chrt可能静默降级为SCHED_OTHER
调度时机不是“定时闹钟”,而是事件驱动
调度器不靠固定间隔唤醒检查,而是在关键事件点触发重调度判断:系统调用返回、中断退出、进程主动sleep、时间片耗尽(由timer interrupt通知)、甚至另一个CPU上的进程被唤醒(wake_up_process)——只要任一CPU发现当前current进程的vruntime已明显高于队列最小值,就设TIF_NEED_RESCHED标志,等最近一次中断/系统调用返回时立即切换。
这意味着:一个死循环的CPU密集型进程,若没系统调用也没中断,可能霸占CPU数毫秒(取决于CONFIG_HZ配置);而一个频繁read()磁盘的进程,每次阻塞后唤醒都会触发快速重调度,响应极快。别迷信“10ms调度粒度”,实际延迟由工作负载特征决定。
别忽略 SCHED_IDLE 和 SCHED_BATCH 的真实用途
SCHED_IDLE不是“最低优先级”,而是仅在系统空闲时才允许运行——即使CPU利用率99%,它也拿不到时间片。适合后台日志归档、低优先级监控脚本这类“绝不抢资源”的任务。而SCHED_BATCH是给CPU密集型批处理设计的:它主动避免抢占(减少上下文切换开销),但依然走CFS队列,只是唤醒时不触发立即调度。
典型误用:nohup python train.py & 后随手chrt -i 0 → 本想降低影响,结果训练进程几乎不运行;正确做法是保持SCHED_OTHER,用renice 19压低优先级,或用cgroups v2限频更可控。
真正难调的从来不是“怎么设优先级”,而是理解不同调度类之间的隔离边界——CFS进程再低的nice也抢不过一个SCHED_FIFO进程,而SCHED_DEADLINE进程甚至能绕过所有优先级规则。混用策略前,先确认你的场景是否真的需要跨类协作。










