- A+
所属分类:Linux系统
代码分析根据3.10版本
通过对前面的学习我们知道Linux的调度分为两种
- 周期调度 完成周期性算法参数的更新和系统其它实际的检查
- 主调的 真正的调度过程
我们现在来看下主调的的代码框架。
入口
根《调度发生的情况》的学习,我们知道所有的调度最后都会到 schedule 函数中。因此我们就先从schedule函数入手。
asmlinkage void __sched schedule(void) { struct task_struct *tsk = current; sched_submit_work(tsk);//获取当前current 并且加锁。 __schedule(); }
schedule 函数并没有太多的操作,只是获取当前task,然后加锁,最后都交给了**__schedule()**函数。
主要框架
主要函数__schedule分析
static void __sched __schedule(void) { struct task_struct *prev, *next; unsigned long *switch_count; struct rq *rq; int cpu; need_resched: preempt_disable(); cpu = smp_processor_id(); rq = cpu_rq(cpu);//找到当前cpu拿取队列 prev = rq->curr;//保持此时此刻的任务 ..... pre_schedule(rq, prev);//做一些预处理 .... put_prev_task(rq, prev); next = pick_next_task(rq);//选取下一个进程 clear_tsk_need_resched(prev); rq->skip_clock_update = 0; if (likely(prev != next)) {//选好了, rq->nr_switches++; rq->curr = next; ++*switch_count;//计数加一 ... context_switch(rq, prev, next); /* unlocks the rq */真正的切换堆栈帧 ... cpu = smp_processor_id(); rq = cpu_rq(cpu); } else raw_spin_unlock_irq(&rq->lock); .... if (need_resched())//是否需要重新调度 goto need_resched; }
其实**__schedule**还是比较长的,但是我们这里选择了一些主要的流程分析,流程如下:
- 1、拿到当前cpu的队列rq
- 2、进行一些与操作处理
- 3、选取下一个进程
- 4、堆栈帧context_switch 切换
- 5、是否需要重新调度
关于重新调度,设计到一些内核抢占的知识,我们暂时不分析。而context_switch,是硬件堆栈的切换,我们后文会认真分析这个函数,在这个过程中我们主要关注如何选取下一个进程。
如何选取下一个任务 pick_next_task 函数分析
static inline struct task_struct * pick_next_task(struct rq *rq) { const struct sched_class *class; struct task_struct *p; /* * Optimization: we know that if all tasks are in * the fair class we can call that function directly: */ if (likely(rq->nr_running == rq->cfs.h_nr_running)) {检查当前cpu队列中是否所有的都是cfs,如果是就直接掉用cfs的调度类 p = fair_sched_class.pick_next_task(rq); if (likely(p)) return p; } for_each_class(class) {循环遍历每一个调度类 p = class->pick_next_task(rq); if (p) return p; } }
pick_next_task 还是比较简单的只是有两部, 1、查看当前cpu队列是否所有的都是cfs 2、遍历所有调度类。 对于是不是全部cfs的检查是有必要的,会减少开销。我们再来看下遍历
#define sched_class_highest (&stop_sched_class) #define for_each_class(class) \ for (class = sched_class_highest; class; class = class->next) extern const struct sched_class stop_sched_class; extern const struct sched_class rt_sched_class; extern const struct sched_class fair_sched_class; extern const struct sched_class idle_sched_class;
我们看到是从stop_sched_class开始,
const struct sched_class stop_sched_class = { .next = &rt_sched_class, .... } const struct sched_class rt_sched_class = { .next = &fair_sched_class, .... } const struct sched_class fair_sched_class = { .next = &idle_sched_class, .... } const struct sched_class idle_sched_class = { /* .next is NULL */ .... }
可以看到是这么个列表。
stop_sched_class --》rt_sched_class--》fair_sched_class--》idle_sched_class--》NULL
以cfs为例看看如何选取task
static struct task_struct *pick_next_task_fair(struct rq *rq) { struct task_struct *p; struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *se; if (!cfs_rq->nr_running) return NULL; do { se = pick_next_entity(cfs_rq); set_next_entity(cfs_rq, se); cfs_rq = group_cfs_rq(se); } while (cfs_rq); p = task_of(se); if (hrtick_enabled(rq)) hrtick_start_fair(rq, p); return p; }
这部分代码也是组调度流程的基础,现在看起来是比较简单的,但是接下来分析组调度的时候就会详细的展开分析。 我们看这个简单的过程:
- 全局的rq 拿到cfs_rq
- cfs_rq 通过pick_next_entity 拿到算法算好的调度类entity----se。
- 进行组调度----目前先忽略
- 将se 转换成task 这样就结束了主调的的所有过程。
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏