第10章 实时进程管理
基于 Linux 6.12.38 源码分析
10.1 实时调度策略
10.1.1 调度策略类型
位置: include/uapi/linux/sched.h
1 2 3 4 5 6 7 8 9 10
|
#define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 #define SCHED_IDLE 5 #define SCHED_DEADLINE 6 #define SCHED_EXT 7
|
10.1.2 调度策略优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| ┌─────────────────────────────────────────────────────────────┐ │ 调度类优先级层次 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ stop_sched_class (停止任务,最高优先级) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ dl_sched_class (Deadline 调度) │ │ │ │ - EDF (Earliest Deadline First) │ │ │ │ - CBS (Constant Bandwidth Server) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ rt_sched_class (实时调度) │ │ │ │ - SCHED_FIFO (先入先出) │ │ │ │ - SCHED_RR (时间片轮转) │ │ │ │ - 优先级: 0-99 (数值越大优先级越高) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ fair_sched_class (完全公平调度) │ │ │ │ - SCHED_NORMAL (普通进程) │ │ │ │ - SCHED_BATCH (批处理) │ │ │ │ - SCHED_IDLE (空闲) │ │ │ │ - nice 值: -20 到 +19 │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
|
10.2 实时优先级
10.2.1 优先级范围
位置: include/linux/sched.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #define MAX_RT_PRIO 100 #define MAX_USER_RT_PRIO 100 #define MAX_PRIO (MAX_RT_PRIO + 40)
|
10.2.2 优先级转换
1 2 3 4 5 6 7 8
| #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) #define NICE_TO_PRIO(nice) ((nice) + MAX_RT_PRIO + 20)
|
10.2.3 实时优先级数组
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct rt_prio_array { DECLARE_BITMAP(bitmap, MAX_RT_PRIO + 1); struct list_head queue[MAX_RT_PRIO]; };
|
10.3 SCHED_FIFO 策略
10.3.1 FIFO 特点
- 运行直到主动让出 CPU
- 没有时间片限制
- 同优先级时按 FIFO 顺序
- 被高优先级任务抢占后,重新运行时继续执行
10.3.2 FIFO 调度流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| SCHED_FIFO 调度示例:
优先级 80: [Task A] ────────┐ 优先级 70: [Task B] │ 优先级 60: [Task C] │ │ CPU 执行: │ ├─ Task A (一直运行) │ │ │ ├─ 优先级 90 Task D 到达 │ │ → 立即抢占 A │ │ │ ├─ Task D 执行完成 │ │ → A 恢复执行 │ │ │ └─ Task A 主动 yield │ → Task B 开始执行 │
|
10.3.3 FIFO 实现
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| static struct task_struct *pick_next_task_rt(struct rq *rq) { struct rt_prio_array *array = &rq->rt.active; struct task_struct *p; struct rt_rq *rt_rq = &rq->rt; int idx;
idx = sched_find_first_bit(array->bitmap); if (idx >= MAX_RT_PRIO) return NULL;
list_entry(array->queue[idx].next, struct task_struct, rt.run_list);
return p; }
static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct rt_rq *rt_rq = &rq->rt; struct sched_rt_entity *rt_se = &p->rt;
list_add_tail(&rt_se->run_list, &rt_rq->active.queue[p->prio]);
__set_bit(p->prio, rt_rq->active.bitmap);
rt_rq->rt_nr_running++; }
|
10.4 SCHED_RR 策略
10.4.1 RR 特点
- 每个任务有时间片
- 时间片用完后重新排队
- 同优先级任务轮流执行
- 被高优先级任务抢占后,时间片保留
10.4.2 RR 时间片
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10
| int sched_rr_timeslice = RR_TIMESLICE;
#define RR_TIMESLICE (100 * HZ / 1000)
|
10.4.3 RR 调度流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| SCHED_RR 调度示例 (优先级 70, 时间片 100ms):
时间: 0ms 100ms 200ms 300ms 400ms │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ A │ │ B │ │ C │ │ A │ └─────┘ └─────┘ └─────┘ └─────┘
队列变化: 时间 0: [A, B, C] 时间 100: [B, C, A] ← A 时间片用完,移到队列尾部 时间 200: [C, A, B] ← B 时间片用完 时间 300: [A, B, C] ← C 时间片用完
|
10.4.4 RR 实现
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) { struct sched_rt_entity *rt_se = &p->rt;
if (--p->rt.time_slice > 0) return;
p->rt.time_slice = sched_rr_timeslice;
requeue_task_rt(rq, p, 0);
if (p->rt.run_list.next != &rq->rt.active.queue[p->prio]) resched_curr(rq); }
|
10.5 实时调度 API
10.5.1 系统调用
设置调度策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy, struct sched_param __user *, param) { struct task_struct *p; struct sched_param lp; int retval;
p = find_process_by_pid(pid); if (!p) return -ESRCH;
if (copy_from_user(&lp, param, sizeof(struct sched_param))) return -EFAULT;
if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) return -EPERM;
retval = sched_setscheduler(p, policy, &lp);
return retval; }
|
获取调度策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| SYSCALL_DEFINE2(sched_getscheduler, pid_t, pid, struct sched_param __user *, param) { struct task_struct *p; int retval;
p = find_process_by_pid(pid); if (!p) return -ESRCH;
if (param) { struct sched_param lp = { .sched_priority = p->rt_priority }; if (copy_to_user(param, &lp, sizeof(struct sched_param))) return -EFAULT; }
return p->policy; }
|
设置优先级:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param) { struct task_struct *p; struct sched_param lp; int retval;
p = find_process_by_pid(pid); if (!p) return -ESRCH;
if (copy_from_user(&lp, param, sizeof(struct sched_param))) return -EFAULT;
retval = sched_setparam(p, &lp); return retval; }
|
10.5.2 使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <sched.h> #include <stdio.h>
int main(void) { struct sched_param param;
param.sched_priority = 50; if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) { perror("sched_setscheduler"); return 1; }
printf("Scheduler set to SCHED_FIFO, priority %d\n", param.sched_priority);
while (1) { }
return 0; }
|
10.5.3 让出 CPU
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| SYSCALL_DEFINE0(sched_yield) { struct rq *rq = this_rq_lock();
schedstat_inc(rq->yld_count);
current->sched_class->yield_task(rq, current);
__schedule(true);
return 0; }
|
10.6 实时带宽管理
10.6.1 带宽限制
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10 11
|
int sysctl_sched_rt_period = 1000000; int sysctl_sched_rt_runtime = 950000;
|
10.6.2 带宽节流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) { u64 runtime = sched_rt_runtime(rt_rq);
if (runtime == RUNTIME_INF) return 0;
if (rt_rq->rt_time > runtime) { rt_rq->rt_throttled = 1; return 1; }
return 0; }
|
10.6.3 带宽恢复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun) { struct rt_rq *rt_rq;
rt_rq->rt_time = 0;
if (rt_rq->rt_throttled) { rt_rq->rt_throttled = 0; enqueue_throttled_rt_rq(rt_rq); }
return 0; }
|
10.7 Deadline 调度
10.7.1 EDF 算法
位置: kernel/sched/deadline.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| EDF (Earliest Deadline First) 算法:
┌────────────────────────────────────────────────────────────┐ │ Deadline 调度 │ ├────────────────────────────────────────────────────────────┤ │ │ │ Task A: runtime=20ms, deadline=100ms, period=100ms │ │ Task B: runtime=30ms, deadline=150ms, period=150ms │ │ Task C: runtime=40ms, deadline=120ms, period=120ms │ │ │ │ 时间 0ms: │ │ 选择 Task A (deadline=100ms) │ │ │ │ 时间 20ms: │ │ Task A 完成,剩余: │ │ Task B (deadline=150ms), Task C (deadline=120ms) │ │ 选择 Task C (更早的 deadline) │ │ │ │ 时间 60ms: │ │ Task C 完成,选择 Task B │ │ │ └────────────────────────────────────────────────────────────┘
|
10.7.2 Deadline 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct sched_dl_entity { struct rb_node rb_node;
u64 dl_runtime; u64 dl_deadline; u64 dl_period; u64 dl_bw;
unsigned int dl_throttled:1; unsigned int dl_boosted:1; unsigned int dl_yielded:1;
struct dl_rq *dl_rq; struct dl_rq *my_q; };
|
10.7.3 带宽检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static int dl_check_constraints(struct sched_dl_entity *dl_se) { u64 period = dl_se->dl_period; u64 runtime = dl_se->dl_runtime; u64 new_bw;
new_bw = to_ratio(period, runtime);
if (new_bw > to_ratio(global_rt_period(), global_rt_runtime())) return -EBUSY;
return 0; }
|
10.7.4 Deadline API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct sched_attr { uint32_t size; uint32_t sched_policy; uint64_t sched_flags; uint32_t sched_nice; uint32_t sched_priority; uint64_t sched_runtime; uint64_t sched_deadline; uint64_t sched_period; };
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags); int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags);
|
10.8 实时组调度
10.8.1 实时任务组
位置: kernel/sched/rt.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct rt_rq { struct rt_prio_array active; unsigned long rt_nr_running; u64 rt_time; int rt_throttled;
struct rq *rq; struct task_group *tg;
#ifdef CONFIG_RT_GROUP_SCHED u64 rt_runtime; u64 rt_period; raw_spinlock_t rt_runtime_lock; struct hrtimer rt_period_timer; #endif };
|
10.8.2 cgroup 实时控制
1 2 3 4 5 6 7 8 9 10 11
| mkdir /sys/fs/cgroup/cpu/rt_group
echo 500000 > /sys/fs/cgroup/cpu/rt_group/cpu.rt_runtime_us
echo 1000000 > /sys/fs/cgroup/cpu/rt_group/cpu.rt_period_us
echo 1234 > /sys/fs/cgroup/cpu/rt_group/cgroup.procs
|
10.9 优先级继承
10.9.1 优先级反转问题
1 2 3 4 5 6 7 8 9
| 优先级反转场景:
高优先级任务 A (prio 90) ──→ 等待互斥锁 (被 C 持有) │ 中优先级任务 B (prio 60) ──→ 抢占 C │ 低优先级任务 C (prio 30) ──→ 持有锁,被 B 抢占
问题: A 等待 C,但 C 被 B 抢占,A 无法执行
|
10.9.2 优先级继承解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void rt_mutex_set_prio(struct task_struct *task, int prio) { task->prio = prio; task->rt_priority = prio; task->normal_prio = prio;
if (task->on_rq) { enqueue_task(rq, task, ENQUEUE_RESTORE); if (task->prio < rq->curr->prio) resched_curr(rq); } }
|
10.10 实时调度调优
10.10.1 /proc/sys/kernel 参数
1 2 3 4 5 6 7 8 9 10 11 12 13
| /proc/sys/kernel/sched_rt_period_us
/proc/sys/kernel/sched_rt_runtime_us 设置为 -1 表示无限制
/proc/sys/kernel/sched_rr_timeslice_ms
/proc/sys/kernel/sched_deadline_period_max_us /proc/sys/kernel/sched_deadline_period_min_us
|
10.10.2 chrt 工具
1 2 3 4 5 6 7 8 9 10 11 12
| chrt -f 50 command chrt -r 60 command
chrt -p 1234
chrt -d --sched-runtime 20000000 \ --sched-deadline 100000000 \ --sched-period 100000000 \ 0 command
|
10.10.3 实时任务最佳实践
CPU 隔离
1 2 3
| echo 2-3 > /sys/devices/system/cpu/no_tick_full echo 2-3 > /sys/devices/system/cpu/isolated
|
IRQ 亲和性
1 2
| echo 1 > /proc/irq/123/smp_affinity_list
|
内存锁定
1 2
| mlockall(MCL_CURRENT | MCL_FUTURE);
|
10.11 本章小结
本章介绍了 Linux 实时进程管理:
- 调度策略:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE
- 优先级范围:实时 0-99,普通 100-139
- FIFO 调度:运行直到主动让出
- RR 调度:时间片轮转
- 实时 API:sched_setscheduler、sched_setparam
- 带宽管理:限制实时任务 CPU 使用率
- Deadline 调度:EDF 算法,基于截止时间
- 组调度:cgroup 实时任务组
- 优先级继承:解决优先级反转
- 调优参数:/proc、chrt、CPU 隔离
下一章将介绍进程资源限制与统计。