在Linux中内核提供了两个调度器主调度器,周期性调度器
主调度器是直接的, 比如进程打算睡眠或出于其他原因放弃CPU,schedule函数
周期性调度器是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要,scheduler_tick查看当前进程是否运行太长时间,如果是,将进程的TIF_NEED_RESCHED置位,然后再中断返回时,调用schedule,进行进程切换操作
两个调度器结合起来称为核心调度器
/*
* schedule() is the main scheduler function.
*/
asmlinkage 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();
//获得当前cpu的运行队列
rq = cpu_rq(cpu);
//增加当前quiescent state计数(RCU锁相关)
rcu_qsctr_inc(cpu);
prev = rq->curr;
switch_count = &prev->nivcsw;
//释放当前进程的大内核锁
release_kernel_lock(prev);
need_resched_nonpreemptible:
schedule_debug(prev);
hrtick_clear(rq);
/*
* Do the rq-clock update outside the rq lock:
*/
//关闭中断
local_irq_disable();
update_rq_clock(rq);
spin_lock(&rq->lock);
//清除当前进程的need_resched标志
clear_tsk_need_resched(prev);
//如果当前进程不可运行并且在内核态没有被抢占
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
//如果当前进程处于等待信号的状态,就不能把它移出就绪队列,还要把状态设置为就绪状态
if (unlikely(signal_pending_state(prev->state, prev)))
prev->state = TASK_RUNNING;
else
//把当前进程移出就绪队列
deactivate_task(rq, prev, 1);
switch_count = &prev->nvcsw;
}
#ifdef CONFIG_SMP
if (prev->sched_class->pre_schedule)
prev->sched_class->pre_schedule(rq, prev);
#endif
if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
//将切换出去的进程插到队尾
prev->sched_class->put_prev_task(rq, prev);
//挑选下一个最合适的进程执行
next = pick_next_task(rq, prev);
if (likely(prev != next)) {
//更新调度相关信息(时间片)
sched_info_switch(prev, next);
rq->nr_switches++;
rq->curr = next;
++*switch_count;
//上下文切换
context_switch(rq, prev, next); /* unlocks the rq */
/*
* the context switch might have flipped the stack from under
* us, hence refresh the local variables.
*/
cpu = smp_processor_id();
rq = cpu_rq(cpu);
} else
spin_unlock_irq(&rq->lock);
hrtick_set(rq);
if (unlikely(reacquire_kernel_lock(current) < 0))
goto need_resched_nonpreemptible;
preempt_enable_no_resched();
if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
goto need_resched;
}
/*
* This function gets called by the timer code, with HZ frequency.
* We call it with interrupts disabled.
*
* It also gets called by the fork code, when changing the parent's
* timeslices.
*/
void scheduler_tick(void)
{
int cpu = smp_processor_id();
//获得当前cpu的就绪队列
struct rq *rq = cpu_rq(cpu);
struct task_struct *curr = rq->curr;
sched_clock_tick();
spin_lock(&rq->lock);
// 更新rq的当前时间戳.即使rq->clock变为当前时间戳
update_rq_clock(rq);
update_cpu_load(rq);
//执行当前运行进程所在调度类的task_tick函数进行周期性调度
curr->sched_class->task_tick(rq, curr, 0);
spin_unlock(&rq->lock);
#ifdef CONFIG_SMP
//当前CPU是否空闲
rq->idle_at_tick = idle_cpu(cpu);
//如果到是时候进行周期性负载平衡则触发SCHED_SOFTIRQ
trigger_load_balance(rq, cpu);
#endif
}