Linux内核分析之进程管理-10

韩乔落

第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 /* 普通分时进程 (CFS) */
#define SCHED_FIFO 1 /* 先进先出实时调度 */
#define SCHED_RR 2 /* 轮转实时调度 */
#define SCHED_BATCH 3 /* 批处理进程 (类似普通但不是交互式) */
#define SCHED_IDLE 5 /* 空闲进程 (极低优先级) */
#define SCHED_DEADLINE 6 /* Deadline 调度 */
#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) /* 总优先级级数 */

/*
* 实时优先级: 0-99
* - 数值越大,优先级越高
* - 99 是最高实时优先级
* - 0 是最低实时优先级
*
* 普通优先级: 100-139
* - 映射到 nice 值: -20 到 +19
* - nice 值越小,优先级越高
*/

10.2.2 优先级转换

1
2
3
4
5
6
7
8
// nice 值转静态优先级
#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) // static_prio -> nice
#define NICE_TO_PRIO(nice) ((nice) + MAX_RT_PRIO + 20) // nice -> static_prio

// 示例:
// nice -20 -> static_prio = 100 (最高普通优先级)
// nice 0 -> static_prio = 120
// nice +19 -> static_prio = 139 (最低普通优先级)

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]; /* 每个优先级的任务队列 */
};

/*
* bitmap: 快速查找最高优先级
* - bit[i] = 1 表示优先级 i 有任务
* - 使用 ffq(bitmap) 找到最高优先级
*
* queue[i]: 优先级 i 的任务链表
* - FIFO: 按 fork 顺序排列
* - RR: 时间片用完后移到队列尾部
*/

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;

// 1. 查找最高优先级
idx = sched_find_first_bit(array->bitmap);
if (idx >= MAX_RT_PRIO)
return NULL;

// 2. 获取该优先级队列的第一个任务
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;  /* 默认 100ms */

/*
* RR 时间片定义
*/
#define RR_TIMESLICE (100 * HZ / 1000) /* 100ms */

/*
* 可以通过 /proc/sys/kernel/sched_rr_timeslice_ms 调整
*/

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
// kernel/sched/core.c
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;

// 设置为 FIFO 策略,优先级 50
param.sched_priority = 50;
if (sched_setscheduler(0, SCHED_FIFO, &param) == -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
// kernel/sched/core.c
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
/*
* 实时调度周期与运行时间
* 默认:1秒周期,0.95秒运行时间
*/
int sysctl_sched_rt_period = 1000000; /* 1 秒 */
int sysctl_sched_rt_runtime = 950000; /* 0.95 秒 */

/*
* 这确保实时任务不会占用超过 95% 的 CPU
* 至少保留 5% 给普通任务
*/

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
// include/linux/sched.h
struct sched_dl_entity {
struct rb_node rb_node; /* 红黑树节点 */

u64 dl_runtime; /* 执行时间预算 */
u64 dl_deadline; /* 绝对截止时间 */
u64 dl_period; /* 周期 */
u64 dl_bw; /* 带宽 = runtime / period */

unsigned int dl_throttled:1; /* 是否被节流 */
unsigned int dl_boosted:1; /* 是否被提升 */
unsigned int dl_yielded:1; /* 是否主动让出 */

struct dl_rq *dl_rq; /* Deadline 运行队列 */
struct dl_rq *my_q; /* 组调度 */
};

10.7.3 带宽检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// kernel/sched/deadline.c
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
// 设置 Deadline 调度
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; /* 运行时间 (ns) */
uint64_t sched_deadline; /* 截止时间 (ns) */
uint64_t sched_period; /* 周期 (ns) */
};

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

# 设置实时运行时间 (500ms)
echo 500000 > /sys/fs/cgroup/cpu/rt_group/cpu.rt_runtime_us

# 设置实时周期 (1000ms)
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
// kernel/locking/rtmutex.c
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);
}
}

/*
* 继承链示例:
* A (prio 90) 等待锁 (被 C 持有)
* -> C 继承 A 的优先级 90
* -> C 快速执行并释放锁
* -> A 恢复执行,优先级恢复为 90
*/

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 # 默认 1000000 (1秒)

# 实时调度运行时间 (微秒)
/proc/sys/kernel/sched_rt_runtime_us # 默认 950000 (0.95秒)
设置为 -1 表示无限制

# RR 时间片 (毫秒)
/proc/sys/kernel/sched_rr_timeslice_ms # 默认 100

# Deadline 周期限制
/proc/sys/kernel/sched_deadline_period_max_us # 默认 4194304 (~4秒)
/proc/sys/kernel/sched_deadline_period_min_us # 默认 100

10.10.2 chrt 工具

1
2
3
4
5
6
7
8
9
10
11
12
# 设置实时优先级
chrt -f 50 command # FIFO 策略,优先级 50
chrt -r 60 command # RR 策略,优先级 60

# 查看当前调度策略
chrt -p 1234

# 设置 Deadline 调度
chrt -d --sched-runtime 20000000 \
--sched-deadline 100000000 \
--sched-period 100000000 \
0 command

10.10.3 实时任务最佳实践

  1. CPU 隔离

    1
    2
    3
    # 隔离 CPU 2-3 用于实时任务
    echo 2-3 > /sys/devices/system/cpu/no_tick_full
    echo 2-3 > /sys/devices/system/cpu/isolated
  2. IRQ 亲和性

    1
    2
    # 将 IRQ 绑定到非隔离 CPU
    echo 1 > /proc/irq/123/smp_affinity_list
  3. 内存锁定

    1
    2
    // 锁定内存,避免缺页
    mlockall(MCL_CURRENT | MCL_FUTURE);

10.11 本章小结

本章介绍了 Linux 实时进程管理:

  1. 调度策略:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE
  2. 优先级范围:实时 0-99,普通 100-139
  3. FIFO 调度:运行直到主动让出
  4. RR 调度:时间片轮转
  5. 实时 API:sched_setscheduler、sched_setparam
  6. 带宽管理:限制实时任务 CPU 使用率
  7. Deadline 调度:EDF 算法,基于截止时间
  8. 组调度:cgroup 实时任务组
  9. 优先级继承:解决优先级反转
  10. 调优参数:/proc、chrt、CPU 隔离

下一章将介绍进程资源限制与统计。

  • Title: Linux内核分析之进程管理-10
  • Author: 韩乔落
  • Created at : 2026-01-14 19:20:30
  • Updated at : 2026-01-19 13:40:23
  • Link: https://jelasin.github.io/2026/01/14/Linux内核分析之进程管理-10/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments