schedule函数在内核的kernel/sched/core.c位置,内核版本是4.1.15
schedule函数主要完成调度的工作。在这个函数中主要关注两个函数,一个是__schedule,一个是need_resched。这篇文章主要关注__schedule。
__schedule函数中分了两部分。第一部分获取cpu的id以及该cpu的进程就绪队列。第二部分获取下一个进程,需要被执行的进程。
第一部分
preempt_disable();
cpu = smp_processor_id();
rq = cpu_rq(cpu);
rcu_note_context_switch();
prev = rq->curr;
schedule_debug(prev);
if (sched_feat(HRTICK))
hrtick_clear(rq);
/*
* Make sure that signal_pending_state()->signal_pending() below
* can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
* done by the caller to avoid the race with signal_wake_up().
*/
smp_mb__before_spinlock();
raw_spin_lock_irq(&rq->lock);
这是内核__schedule函数中的代码,对应我所谓的第一部分。先获取到当前cpu的id,然后获取出当前cpu的就绪队列(也就是需要被执行的进程的队列),从而获取到当前进程。
第二部分
switch_count = &prev->nivcsw;
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
if (unlikely(signal_pending_state(prev->state, prev))) {
prev->state = TASK_RUNNING;
} else {
deactivate_task(rq, prev, DEQUEUE_SLEEP);
prev->on_rq = 0;
/*
* If a worker went to sleep, notify and ask workqueue
* whether it wants to wake up a task to maintain
* concurrency.
*/
if (prev->flags & PF_WQ_WORKER) {
struct task_struct *to_wakeup;
to_wakeup = wq_worker_sleeping(prev, cpu);
if (to_wakeup)
try_to_wake_up_local(to_wakeup);
}
}
switch_count = &prev->nvcsw;
}
if (task_on_rq_queued(prev))
update_rq_clock(rq);
next = pick_next_task(rq, prev);
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
rq->clock_skip_update = 0;
第二部分if里的代码查资料看了,但是还没看懂,我关注了next = pick_next_task(rq, prev);,其中pick_next_task函数就是找下一个需要执行的进程。这个函数也定义在内核的kernel/sched/core.c中。代码如下:
static inline struct task_struct *
pick_next_task(struct rq *rq, struct task_struct *prev)
{
const struct sched_class *class = &fair_sched_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(prev->sched_class == class &&
rq->nr_running == rq->cfs.h_nr_running)) {
p = fair_sched_class.pick_next_task(rq, prev);
if (unlikely(p == RETRY_TASK))
goto again;
/* assumes fair_sched_class->next == idle_sched_class */
if (unlikely(!p))
p = idle_sched_class.pick_next_task(rq, prev);
return p;
}
again:
for_each_class(class) {
p = class->pick_next_task(rq, prev);
if § {
if (unlikely(p == RETRY_TASK))
goto again;
return p;
}
}
BUG(); /* the idle class will always have a runnable task */
}
首先const struct sched_class *class = &fair_sched_class;获取了一个算法类(不同的进程使用不同的算法来找下一个需要被执行的进程),fair_sched_class这个通过百度得知,是公平算法类,所以说默认先是公平算法类。接下来判断当前进程的算法类是不是公平算法类且就绪队列里的进程个数等于公平算法类进程个数,如果是,则说明所有就绪的进程都可以使用公平算法类来查找,那就直接调用公平算法,也就是p = fair_sched_class.pick_next_task(rq, prev); 如果不是那就轮询所有的算法类(for_each_class(class)),找出下一个进程。
这里有个结构很重要,rq结构体(kernel/sched/sched.h)。包含了 公平运行队列cfs,实时运行队列rt,期限运行队列dl,停机调度类和空闲调度类。这对应 rq->nr_running == rq->cfs.h_nr_running。
以上只是个人拙见,内核中的冰山一角中的一角,还在持续阅读中,若有错误还望指正。
与君共勉