(1) 调度类用于判断接下来运行哪个进程,内核支持不同的调度策略(完全公平调度,实时调度);调度类使得能够以模块化方法实现这些策略;
(2) 在选中将要选择的进程后,必须执行底层任务切换;需要与cpu的紧密交互;
每个进程都刚好属于某一调度类,各个调度类负责管理所属的进程;通用调度器自身完全不涉及进程管理,其工作都委托给调度器类;
1. task_struct的成员
各个进程的task_struct有几个成员与调度相关;
struct task_struct {
...
int prio, static_prio, normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
unsigned int policy;
cpumask_t cpus_allowed;
};
prio和normal_prio表示动态优先级;其中normal_prio表示基于进程的静态优先级和调度策略计算出的优先级; prio中保存了调度器考虑的优先级,
static_prio表静态优先级;静态优先级是进程启动时分配的优先级,它可以用nice和sched_setscheduler系统调用修改;否则在进程运行间保存恒定;
rt_priority表示实时进程的优先级;最低0,最高99;值越大,表明优先级越高;
sched_class表示该进所属的调度器类;
se:表示可调度实体;相当于在struct task_struct结构体中内嵌一个实例;
policy:保存了对该进程应用的调度策略;有以下几种;
#define SCHED_NORMAL 0 //用于普通进程;
//用于实现软实时进程,RR实现了循环方法,FIFO使用先进先出几种机制;
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3//用于非交互,cpu使用密集的批处理进程;
#define SCHED_IDLE 5/* SCHED_ISO: reserved but not implemented yet */
辅助函数 static inline int rt_policy(int policy) 和 static inline int task_has_rt_policy(struct task_struct *p)用于判断给出的调度策略(或进程)是否属于实时类;
cpus_allowed是一个位域,在多处理上用来限制进程可以在哪些cpu上运行;sched_setaffinity(pid_t pid, const struct cpumask * in_mask)系统调用函数用于设置该位图;
2. 调度器类
调度器类提供了通用调度器和各个调度方法间的关联;
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);
bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt);
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_curr_task) (struct rq *rq);
void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
void (*task_fork) (struct task_struct *p);
void (*switched_from) (struct rq *this_rq, struct task_struct *task);
void (*switched_to) (struct rq *this_rq, struct task_struct *task);
void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
int oldprio);
unsigned int (*get_rr_interval) (struct rq *rq,
struct task_struct *task);
};
next: 成员将各个调度器实例连接起来;
enqueue_task: 向就绪队列添加一个新进程;进程从睡眠态到可运行态发生;
dequeue_task: 将一个进程从就绪队列去除,进程从可运行到不可运行态时就会发生;
yield_task: 在进程想要自动放弃对处理器的控制权时,使用sched_yield系统调用,然后内核调用yield_task;
check_preempt_curr:用一个新唤醒的进程来抢占当前进程;如在wake_up_new_task唤醒新进程时会调用该函数;
check_preempt_curr: 用一个新唤醒的进程抢占当前进程;
pick_next_task: 选择下一个将要运行的进程;
put_prev_task:用另一个进程代替当前进程前调用;
set_curr_task:在进程的调度策略发生变换时调用;
task_tick:每次激活周期性调度器时,由周期性调度器调用;
task_fork:用于建立fork系统调用和调度器的关联;每次新进程建立后,用task_fork通知调度器;
各个调度类都必须提供一个sched_class实例;调度器层次结构:实时进程>完全公平进程>空闲进程
fair_sched_class 表示完全公平调度器实例;
rt_sched_class 表示实时调度器实例;
3. 就绪队列
核心调度器用于管理活动进程的主要数据结构称为就绪队列; 各个cpu都有自身的就绪队列;各个活动进程只出现在一个就绪队列中;
就绪队列使用struct rq表示:
struct rq {
/* runqueue lock: */
raw_spinlock_t lock;
/*
* nr_running and cpu_load should be in the same cacheline because
* remote CPUs use both these fields when doing load calculation.
*/
unsigned long nr_running;
#define CPU_LOAD_IDX_MAX 5
unsigned long cpu_load[CPU_LOAD_IDX_MAX];
unsigned long last_load_update_tick;
int skip_clock_update;
/* capture load from *all* tasks on this cpu: */
struct load_weight load;
unsigned long nr_load_updates;
u64 nr_switches;
struct cfs_rq cfs;
struct rt_rq rt;
unsigned long nr_uninterruptible;
struct task_struct *curr, *idle, *stop;
unsigned long next_balance;
struct mm_struct *prev_mm;
u64 clock;
u64 clock_task;
atomic_t nr_iowait;
/* calc_load related fields */
unsigned long calc_load_update;
long calc_load_active;
};
nr_running: 指定了队列上可运行进程的数目;不考虑优先级和调度类;
load: 提供了就绪队列当前负荷的度量;负荷本质与队列当前活动进程数成正比;
cpu_load: 用于跟踪此前的负荷状态;
cfs和rt是嵌入的子就绪队列,分别用于完全公平调度器和实时调度器;
curr:指向运行的进程的task_struct实例;
idle:指向idle进行的task_struct实例;该进程亦称为idle线程,在无其他可运行进程时执行;
clock: 用于实现就绪队列自身的时钟;
系统中所有就绪队列都在runqueues数组中,该数组的每个元素分别对应系统的一个cpu;
DECLARE_PER_CPU(struct rq, runqueues);
#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu)))
#define this_rq() (&__get_cpu_var(runqueues))
#define task_rq(p) cpu_rq(task_cpu(p))
#define cpu_curr(cpu) (cpu_rq(cpu)->curr)
#define raw_rq() (&__raw_get_cpu_var(runqueues))
4. 调度实体
用struct sched_entity表示调度实体;
struct sched_entity {
struct load_weight load; /* for load-balancing */
struct rb_node run_node;
struct list_head group_node;
unsigned int on_rq;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 prev_sum_exec_runtime;
u64 nr_migrations;
...
};
load 表示权重,决定实体占队列总负荷的比例;
run_node: 标准的数节点,使得实体可以在红黑树排序;
on_rq: 该实体当前是否在就绪队列上接收调度器调度;
sum_exec_runtime:记录消耗的cpu时间,以用于完全公平调度器;
prev_sum_exec_runtime:进程被撤销cpu时,sum_exec_runtime值保存到prev_sum_exec_runtime;
由于每个task_struct中都嵌入了sched_entity实例,所以进程是可调度实体;