进程调度
一、调度器演化
- O(1) 调度器(2.6 早期)
- CFS(Completely Fair Scheduler)(2.6.23 起,主力)
- BFS / MuQSS(Con Kolivas,社区)
- EEVDF(6.6 起,默认调度)
二、CFS 核心概念
1. 关键思想
- 给每个任务一个"虚拟运行时间"(vruntime)
- 总是选择 vruntime 最小的任务运行
- 公平:长时间未运行的任务优先
2. 关键数据结构
struct task_struct {
int prio; // 优先级
int static_prio;
int normal_prio;
unsigned int policy; // SCHED_NORMAL / FIFO / RR / DEADLINE / IDLE
int on_rq;
struct sched_entity se; // 调度实体
struct sched_rt_entity rt;
// ...
};
struct sched_entity {
u64 vruntime; // 虚拟运行时间
u64 sum_exec_runtime;
u64 prev_sum_exec_runtime;
// ...
};
struct rq {
struct cfs_rq cfs;
struct rt_rq rt;
unsigned long nr_running;
// ...
};
struct cfs_rq {
struct rb_root tasks_timeline; // 红黑树
struct sched_entity *curr;
u64 min_vruntime;
};
3. 优先级与权重
- nice 值 -20 ~ 19(默认 0)
- 越低 nice 值,权重越高
- 权重表(数组):
sched_prio_to_weight - nice 0 → weight 1024
- nice -20 → 1024 / 0.32 ≈ 88761
- nice +19 → 1024 / 1.97 ≈ 15
4. 时间片
- 默认
sysctl_sched_latency = 6ms - 每个任务时间片 = latency × (weight / 总权重)
- 任务数多时缩短:
min_granularity = 0.75ms
三、调度策略
| 策略 | 说明 |
|---|---|
SCHED_NORMAL |
普通任务(CFS) |
SCHED_BATCH |
后台批处理(CFS) |
SCHED_IDLE |
极低优先级 |
SCHED_FIFO |
实时,先入先出 |
SCHED_RR |
实时,时间片轮转 |
SCHED_DEADLINE |
截止时间调度(CBS) |
四、调度类(sched_class)
struct sched_class {
const struct sched_class *next;
void (*enqueue_task)(struct rq *rq, struct task_struct *p, int flags);
void (*dequeue_task)(struct rq *rq, struct task_struct *p, int flags);
void (*yield_task)(struct rq *rq, struct task_struct *p);
void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);
struct task_struct *(*pick_next_task)(struct rq *rq);
void (*put_prev_task)(struct rq *rq, struct task_struct *p);
void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
// ...
};
调度类优先级:
stop_sched_class → 最高(CPU 停止)
dl_sched_class → deadline
rt_sched_class → SCHED_FIFO/RR
fair_sched_class → SCHED_NORMAL
idle_sched_class → 最低
五、调度点
- 主动调度:
schedule()函数 - 抢占调度:更高优先级任务进入可运行队列时
- 周期性调度:scheduler_tick → check_preempt_tick
抢占模型(preempt 模型)
PREEMPT_NONE:老内核,不可抢占PREEMPT_VOLUNTARY:自愿点(推荐)PREEMPT__LL、PREEMPT_RT:可抢占
查看:zcat /proc/config.gz | grep PREEMPT
六、CFS 调优参数
# /proc/sys/kernel/sched_*
sched_latency_ns # 调度周期
sched_min_granularity_ns # 最小时间片
sched_wakeup_granularity_ns
sched_child_runs_first
sched_tunable_scaling # 0/1
sched_migration_cost_ns
sched_cfs_bandwidth_slice_us
七、实时调度
struct sched_param sp = { .sched_priority = 80 };
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
// 80/99 普通实时;99 最高
// 进程
sched_setscheduler(pid, SCHED_FIFO, &sp);
注意:
- 需要 CAP_SYS_NICE 权限
- 优先级反转(priority inversion):用优先级继承(mutex)
- 实时任务不能睡眠
八、组调度(cgroup)
# 创建 cpu cgroup
mkdir /sys/fs/cgroup/cpu/mygroup
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.shares # 权重
echo 100000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us
echo $$ > /sys/fs/cgroup/cpu/mygroup/tasks
九、CPU 亲和性
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
CPU_SET(2, &set);
sched_setaffinity(0, sizeof(set), &set);
十、调度延迟监控
# 调度延迟直方图
cat /proc/sys/kernel/sched_latency_ns
cat /proc/sched_debug
# perf
perf stat -e sched:sched_wakeup -e sched:sched_switch sleep 1
perf sched record sleep 5
perf sched latency -s max ./prog
十一、嵌入式注意点
- 实时需求 →
PREEMPT_RT内核或 Xenomai - 多核 → 关注 SMP 调度均衡
- 异构(big.LITTLE)→ 用 EAS(Energy Aware Scheduling)
- 关闭无关 CPU(省电):
echo 0 > /sys/devices/system/cpu/cpu1/online