task_struct 中的调度信息

1 task_struct调度信息

上一节我们说了,task_struct是内核真正的调度对象,代表一个可以被独立执行、阻塞、唤醒和调度的任务。
完整的 task_struct非常庞大,包含一个 task 的各类内核信息。
比如身份信息,内存信息,调度信息等。

调度器拿到一个task后需要根据携带的调度信息进行判断。
其中携带的核心调度信息如下

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
struct task_struct {
/* 任务状态 */
unsigned int __state;

/* 调度策略 */
unsigned int policy;

/* 优先级 */
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;

/* 调度类 */
const struct sched_class *sched_class;

/* 不同调度类使用的调度实体 */
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;

/* runqueue状态 */
int on_rq;

/* CPU归属和CPU亲和性 */
const cpumask_t *cpus_ptr;
cpumask_t cpus_mask;
int nr_cpus_allowed;

...
};

1. policy调度策略

常见的调度策略定义为

1
2
3
4
5
6
7
#define SCHED_NORMAL    0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6

其分类如图

2. 优先级

这几个优先级之前我们讲过,在回顾一下

  • static_prio:普通任务的静态基础值,主要来自nice
  • rt_priority:实时任务的用户可见实时优先级
  • normal_prio:根据policy、nice、rt_priority等计算出的正常内部优先级
  • prio:调度器当前真正生效的内部优先级

先看prio内部优先级

1
2
3
#define MAX_RT_PRIO    100
#define MAX_PRIO (MAX_RT_PRIO + 40)
#define DEFAULT_PRIO (MAX_RT_PRIO + 20)

其分布如图所示

对于用户空间,看见的就是nice值,范围在-20~19,static_prio尤其转换来。

整个优先级的分布和转换如图所示

3. 调度类

核心字段:

1
const struct sched_class *sched_class;

就是调度算法的具体操作,怎么调度。决定task怎样入队和出队,如何判断抢占等操作。

4. 调度实体

被调度的具体对象

1
2
3
struct sched_entity *parent;
struct cfs_rq *cfs_rq;
struct cfs_rq *my_q;

整体框架

2 runqueue

之前我们说了,per-CPU都有一个管理就绪任务的运行队列。
虽然叫队列,但他的底层并没有队列那么简单。其核心功能主要有以下几点

  1. 保存可运行任务集合
  2. 按调度类组织任务
  3. 保存当前任务
  4. 保存CPU局部调度状态
  5. 为选择下一个任务提供选择依据
    其简化模型如下

对应核心代码

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
struct rq {
raw_spinlock_t __lock; //rq 自旋锁

unsigned int nr_running; //计数

//调度类子队列
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;

struct task_struct *curr; //当前运行的任务
struct task_struct *idle; //idle task
struct task_struct *stop; //stop task

//调度时钟
u64 clock;
u64 clock_task;

//调度统计
unsigned long nr_uninterruptible;
atomic_t nr_iowait;

/* load / utilization */
/* scheduling statistics */
/* load balance */
/* task migration */
/* CPU topology */
/* uclamp */
/* NOHZ */
/* conditionally compiled fields */
};

3 shed_class

调度的核心,就是为每一种调度算法提供统一的回调函数。
其核心结构如下

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
33
34
35
36
37
38
struct sched_class {
//入队和出队
void (*enqueue_task)(struct rq *rq,
struct task_struct *p,
int flags);

void (*dequeue_task)(struct rq *rq,
struct task_struct *p,
int flags);

//主动让出CPU
void (*yield_task)(struct rq *rq);

//抢占检查
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 (*set_next_task)(struct rq *rq,
struct task_struct *p,
bool first);

//周期性调度
void (*task_tick)(struct rq *rq,
struct task_struct *p,
int queued);

/* SMP选核与迁移操作 */
/* task创建、退出操作 */
/* policy/priority改变通知 */
/* 运行时间更新操作 */
};

就是一张调度操作函数指针表。

具体的实现有

以fair_sched_class为例子,其会在以下绑定上面的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DEFINE_SCHED_CLASS(fair) = {

.enqueue_task = enqueue_task_fair,
.dequeue_task = dequeue_task_fair,
.yield_task = yield_task_fair,
.yield_to_task = yield_to_task_fair,

.wakeup_preempt = check_preempt_wakeup_fair,

.pick_next_task = __pick_next_task_fair,
.put_prev_task = put_prev_task_fair,
.set_next_task = set_next_task_fair,
...
}

task_struct 中的调度信息
https://cj0510.github.io/2026/08/23/Linux Kernel/Scheduling/task_struct 中的调度信息/
作者
CJ1018
发布于
2026年8月23日
许可协议
CC BY-NC-SA 4.0