上一节说调度类绝对隔离:rt 有人,fair 靠边.这话对,但如果只有前半句,一个写坏的实时任务就能把整台机器锁死.这节在单核上把 FIFO 和 RR 的行为全部实测,看隔离的威力,再看内核给这威力套上的那道缰绳 — RT 带宽节流.还有我自己实验里翻的一场好戏,顺便看清 rt 类内部的抢占规则.
这篇回答几件事:
- FIFO 到底"先进先出"到什么程度,等优先级怎么办
- RR 的轮转和 FIFO 差在哪
- 普通任务在实时任务旁边还有没有活路,为什么
- RT 带宽节流怎么运作
实验设置:全钉在 cpu1,一个普通任务和几个实时任务轮番上场,CPU 时间从 /proc/PID/stat 的 utime 差值读出来,毫秒级精度.第一幕,普通任务独占:
A) normal alone: 2010 ms / 2s
满血.第二幕,一个 SCHED_FIFO 优先级 50 的死循环挤进来:
B) normal + FIFO50: normal=100 ms fifo=1910 ms / 2s
普通任务从 2010 掉到 100 毫秒.100 恰好是 2 秒的 5% — 这个数字不是巧合,是内核的刻意设计,后面讲.第三幕,再来一个同优先级的 FIFO:
C) 第二个同优先级 FIFO: 老 FIFO 仍 1900 ms / 2s
新的几乎没得吃.FIFO 的语义到这里见底:先上场的同优先级任务不让位,它不睡不退,后来的就永远排队.rt 类内部的抢占规则是"只认更高的实时优先级",等优先级之间没有绅士风度 — 这和 fair 类"人人有份"的世界观完全相反.
第四幕本来想演 RR 轮转,先翻了个车:场景是"FIFO50 还在场,再加两个 RR50":
D) FIFO50 + 两个 RR50: rr1=90 ms rr2=90 ms / 2s
两个 RR 各只捡到 90 毫秒!清场重测:
D2) 干净环境两个 RR50: rr1=1300 ms rr2=730 ms / 2s
翻车本身就是答案:FIFO50 和 RR50 的"50"是同一个实时优先级,rt 类里等优先级不抢占,所以在跑的 FIFO 压着两个 RR,它们只能去分节流漏下来的残渣.清了 FIFO 之后,RR 的本性露出来 — 两个任务轮流坐庄,份额大致对半(1300/730 的不对称来自起跑时序和 shell 开销).RR 和 FIFO 的唯一区别就在这:同优先级之间,FIFO 是独木桥,RR 是轮盘,内核给 RR 每人一个 timeslice,到期换人,时间片默认 100 毫秒量级.fifo 和 rr 之外还有个 SCHED_DEADLINE,按"每个周期跑多久,最晚什么时候必须跑完"三元组承诺,EDF 最早截止期优先,活在 dl 类,音频工业控制那类硬需求才用,普通发行版内核默认放开但用的人少,这里点到为止.
第五幕,FIFO 30 对 FIFO 60:
E) FIFO30 vs FIFO60: fifo30=290 ms / 2s
低优先级只吃到一点残渣.这里有个细节:FIFO30 理论上应该颗粒无收,那 290 毫秒哪来的?答案和 B 幕里普通任务的 100 毫秒同源 — RT 带宽节流,这篇最值得讲的设计.
问题先摆清楚:调度类绝对隔离意味着 rt 任务可以让 fair 任务颗粒无收.一个忘了 yield 的 FIFO 死循环,就能让 shell 冻住,watchdog 咬人,SSH 都进不去 — Linux 作为通用系统不能这么玩.解法是 rt 运行时配额,两个 sysctl:
/proc/sys/kernel/sched_rt_runtime_us = 950000
/proc/sys/kernel/sched_rt_period_us = 1000000
每个 CPU,每一秒周期,所有 rt 任务加起来最多跑 0.95 秒;超了,这个 CPU 上的 rt 全体被节流挂起,直到下个周期.B 幕的 5%,E 幕的残渣,全是这条规则的余波.它保证了任何 rt 灾难下 fair 类至少有 5% 的窗口 — shell 能敲,能 kill,能自救.要跑到真正的 100%(工控场合),把 runtime 设成 -1 放开,同时你也就交出了那把安全锁.PREEMPT_RT 补丁集则更进一步,把内核里几乎所有自旋锁换成可睡眠锁,让实时任务连内核态的不可抢占区间都大幅缩短,那是另一个故事,主线内核近几年已逐步合入其基础部分.
最后留几个容易忽略的细节:
- chrt 的 -f/-r/-d 三件套分别设 FIFO/RR/DEADLINE,p 查询;普通用户需要 CAP_SYS_NICE 或 RLIMIT_RTPRIO,不是随便谁都能升 rt.
- FIFO 任务的"让位"只有三条路:更高 rt 优先级到来,自己阻塞,节流.没有时间片概念,timeslice 是 RR 独有的.
- rt 任务优先级 1-99,内核内部映射到 prio 0-98,99 保留给迁移线程,你在 /proc 里看到的 -99 大户基本是内核自己人.
- RT 节流是按 CPU 记账的,六核板上六个独立的 95%,把 rt 任务打散到多核不改变单核配额.
- DEADLINE 的三个参数(runtime/period/deadline)交上去后,内核用 EDF 调度并用 CBS(恒定带宽服务器)兜底,任务自己超支会被推到下个 period — 承诺是双向的.
- 任务在 rt 和 fair 之间切换,调度实体是两套账(rt 的 sum_exec_rt 和 fair 的 vruntime 体系),来回切会有冷启动效应,别指望无缝.
- mlockall 配 rt 一起用:页面错误也要等磁盘,那点延迟足够毁掉你的实时预算,内存锁住才是完整套餐.