linux 进程调度源码分析,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 這樣就結束了主調的的所有過程。
總結
以上是生活随笔為你收集整理的linux 进程调度源码分析,Linux调度器源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gnu.getopt java_c –
- 下一篇: c 如何操作php,thinkphp的c