什么是时间片?
时间片(time slice / quantum)是调度器一次分给某个可运行任务连续占用 CPU 的时长。用完就触发一次时钟中断, 调度器把 CPU 收回来交给下一个任务。它的作用是让"单核 CPU 看起来在同时跑很多任务", 本质是用**抢占(preemption)**换取响应速度和公平性。
1. 时间片怎么影响系统
单核、3 个可运行任务、时间片 4ms
时间 →
A(4ms) │ B(4ms) │ C(4ms) │ A(4ms) │ B(4ms) │ C(4ms)
↑每次切换要保存/恢复上下文,并让缓存、TLB 变冷
响应时间 ≈ 时间片 × 前面排队的任务数
吞吐 ≈ 1 / (时间片 + 切换开销) × 有效工作量
| 时间片 | 优点 | 缺点 |
|---|---|---|
| 很小(<1ms) | 交互响应快,公平 | 切换开销占比高,缓存命中率下降 |
| 很大(>100ms) | 吞吐高,缓存友好 | 交互卡顿,一个死循环能饿死别人 |
| 适中(几 ms) | 两者折中 | 需要按负载动态调整 |
切换本身不便宜:保存寄存器、切内核栈、换页表(TLB 失效)、污染分支预测器, 实测一次上下文切换约 2~5 微秒。时间片若只有 1ms,光切换就吃掉 0.5% 以上的 CPU,还不算缓存损失。
2. Linux 的做法:CFS 用"虚拟运行时间"
现代 Linux 默认调度器 CFS(Completely Fair Scheduler)没有固定时间片,
它维护每个任务的 vruntime(虚拟运行时间),永远挑 vruntime 最小的任务跑,
并用红黑树(red-black tree)按 vruntime 排序,取最左节点即 O(log n)。
rb_root
/ \
vruntime小 vruntime大
/ \
(最左) ← 下一个被调度的任务
实际分配时长由两个参数约束:
调度周期 sched_latency(默认 6ms)
若可运行任务数 > sched_latency / sched_min_granularity(默认 0.75ms)
则周期拉长为 任务数 × min_granularity(保证每个任务至少跑 0.75ms)
单任务时间片 = 周期 × 该任务权重 / 所有任务权重之和
权重(weight)由 nice 值映射:nice 每降 1,权重约增加 25%,所以 nice -20 的任务拿到的时间片 大约是 nice 0 的 100 倍量级(权重 88761 对 1024)。
3. 查看与调整
# 调度周期与最小粒度(单位 ns)
cat /proc/sys/kernel/sched_latency_ns # 6000000
cat /proc/sys/kernel/sched_min_granularity_ns # 750000
cat /proc/sys/kernel/sched_wakeup_granularity_ns # 唤醒抢占阈值
# 看一个任务实际跑了多久、被抢占多少次
perf sched latency -p 1234
cat /proc/1234/schedstat # 运行时长(ns) 等待时长(ns) 时间片次数
# 实时任务:SCHED_RR 的固定时间片为 100ms
chrt -r -p 10 1234
chrt -p 1234
容器里更常用 cgroup 的带宽控制,相当于给一组任务设"周期 + 配额":
# 每 100ms 周期内最多用 50ms CPU(约 0.5 核)
echo "50000 100000" > /sys/fs/cgroup/demo/cpu.max
4. 常见追问
Q:时间片用完,任务会怎样?
被标记 TIF_NEED_RESCHED,在返回用户态或中断返回时触发 schedule(),任务放回红黑树(其 vruntime 已推进),
状态变为可运行(R),等下一次被选中。它不会被"杀死",只是让出 CPU。
Q:为什么 CFS 不用固定时间片? 固定片在任务数变化时很难兼顾延迟和吞吐:任务多时响应变差,任务少时切换浪费。 CFS 用"按权重分配比例 + 最小粒度保底"的方式,让时间片随负载自动伸缩,同时保证公平。
Q:实时任务会不会把普通任务饿死?
会,所以内核默认开启 RT throttling(/proc/sys/kernel/sched_rt_runtime_us 默认 950000/1000000),
即每 1 秒最多让实时任务占用 95%,留 5% 给普通任务,避免系统失去响应。
Q:时间片和"调度延迟"是一回事吗? 不是。时间片是单个任务一次跑多久;调度延迟是从"任务变成可运行"到"真正拿到 CPU"的时间, 受队列长度、抢占策略、CPU 亲和性共同影响。压测里关心的 p99 延迟抖动,往往就是调度延迟。
小结
时间片是抢占式调度的最小分配单位,小了响应快但切换贵,大了吞吐高但卡顿。
Linux 用 CFS 的 vruntime + 权重 + 最小粒度 代替固定时间片,并用 cgroup 带宽做配额上限。
答这题时讲清"为什么需要时间片 → 大小如何权衡 → CFS 怎么实现 → 怎么观测"这条线就够了。