全部博文(685)
分类: LINUX
2015-02-05 17:26:36
进程调度
三个组件:调度器、调度器类、上下文切换。
struct task_struct {
...
int prio, static_prio, normal_prio;
unsigned intrt_priority;
struct list_head run_list;
const struct sched_class *sched_class;
struct sched_entity se; //调度器不限于调度进程,可以处理更大的实体(组调度)。因此要求调度器不直接处理进程,而是处理se.
unsigned intpolicy;
cpumask_t cpus_allowed; //sched_setaffinity
unsigned inttime_slice;
...
}
调度器类:
struct sched_class {
conststruct sched_class *next;
void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup);
void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep);
void (*yield_task) (struct rq *rq);
void (*check_preempt_curr) (struct rq *rq, struct task_struct *p);
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);
//每次激活周期性调度器时,由周期性调度器调用task_tick
void (*task_tick) (struct rq *rq, struct task_struct *p);
//fork产生新进程时,用task_new通知调度器
void (*task_new) (struct rq *rq, struct task_struct *p);
};
就绪队列:
struct rq {
unsignedlongnr_running;
#define CPU_LOAD_IDX_MAX 5
unsignedlongcpu_load[CPU_LOAD_IDX_MAX];
...
struct load_weight load;
struct cfs_rq cfs; //嵌入的队列,管理CFS
struct rt_rq rt; //嵌入的队列,管理实时调度
struct task_struct *curr, *idle;
u64 clock;
...
};
定义一个数组runqueues[],每个CPU都有一个struct rq.
static DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
调度实体:
由于调度器可以操作比进程更一般的实体,因此需要一个适当的数据结构表示此类实体。
struct sched_entity {
struct load_weight load; // for load-balancing
struct rb_node run_node;
unsignedinton_rq;
u64exec_start;
u64sum_exec_runtime;
u64vruntime;
u64prev_sum_exec_runtime;
...
}
优先级:
负荷:
进程的优先级不仅由优先级指定,而且还要考虑保存在task_struct中的se.load的负荷权重。set_load_weight负责根据进程类型及其静态优先级计算负荷权重。
负荷权重
struct load_weight {
unsignedlongweight, inv_weight;
};
周期性调度器
按照一定频率自动调度(为省电可以关闭该调度器),在scheduler_tick中实现。
该函数调用委托到的具体底层调度器。
主调度器
内核要将CPU分给另一个进程、从系统调用返回时检查到TIF_NEED_RESCHED时,会调用schedule函数。
__sched前缀:用于标记可能调用schedule的函数(包括schedule函数自己),这些函数被放到sched.text代码段中,内核在显示栈转储时会忽略类似信息。
asmlinkage void __sched schedule(void){
…
prev->sched_class->put_prev_task(rq, prev); //通知调度器当前进程应被替换
next = pick_next_task(rq, prev); //调度器选择下一个应该被执行的进程
…
rq->curr = next;
context_switch(rq, prev, next); //硬件级的进程切换
…
}
上下文切换
static inline void context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next){
…
switch_mm(old_mm, mm, next);
switch_to(prev, next, prev);
barrier();
finish_task_switch(this_rq(), prev);
}
switch_to之后的代码只有在当前进程再次被调度的时候,接着执行。