说明如下引用代码对应的kernel版本是2.6.34
I. Linux进程调度的函数schedule,先看一下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();//禁止抢占,实际操作是将current_thread_info的preempt_count加1,其他进程/线程不会抢占当前线程了
cpu = smp_processor_id();//获取当前CPU号,逻辑CPU号
rq = cpu_rq(cpu); // 获取当前CPU的runqueue队列,该CPU上所有就绪状态的进程放在该队列中
rcu_sched_qs(cpu); //
prev = rq->curr;
switch_count = &prev->nivcsw;
release_kernel_lock(prev);//如果当前进程持有big kernel lock锁则释放,因为当前进程要切换出去了,不能占有该锁
need_resched_nonpreemptible:
schedule_debug(prev);
if (sched_feat(HRTICK))
hrtick_clear(rq);
spin_lock_irq(&rq->lock);//runqueue队列加锁
update_rq_clock(rq);
clear_tsk_need_resched(prev);//清除标志TIF_NEED_RESCHED
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {/*如果当前进程非就绪,并且没有被抢占*/
if (unlikely(signal_pending_state(prev->state, prev)))//如果当前进程是task_interruptible或者有信号送达,状态更改为就绪
prev->state = TASK_RUNNING;
else
deactivate_task(rq, prev, 1); //将当前进程从就绪队列中摘除;非就绪进程怎么会在就绪队列中?就绪任务在获取锁或者主动睡眠时将任务状态修改为非就绪,但是没有立即从就绪队列中出队列,此时才出队列
switch_count = &prev->nvcsw;
}
pre_schedule(rq, prev);
if (unlikely(!rq->nr_running))//如果运行队列上进程数是0,则先通过idle_balance函数从其他CPU上调度,进行负载均衡
idle_balance(cpu, rq);
/*put_prev_task->put_prev_task_fair->put_prev_entity->__enqueue_entity将要调度出去的任务插入红黑树,当然插入红黑树的任务是R状态的,即没有被从就绪队列中摘除的*/
put_prev_task(rq, prev);
next = pick_next_task(rq);//挑选下一个要运行的进程将其排进就绪队列,挑选原则是选择红黑树上的最左边节点进程
if (likely(prev != next)) {
sched_info_switch(prev, next);
perf_event_task_sched_out(prev, next, cpu);
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);
post_schedule(rq);
if (unlikely(reacquire_kernel_lock(current) < 0))
goto need_resched_nonpreemptible;
preempt_enable_no_resched();
if (need_resched())//如果该进程被其他进程设置了TIF_NEED_RESCHED标志,则函数重新执行进行调度
goto need_resched;
}
可见schedule的作用是将当前进程从就绪队列摘除,从就绪队列中选择一个其他符合条件的进程调度运行。何谓符合条件,后面详细分解。其中if (prev->state && !(preempt_count() & PREEMPT_ACTIVE))条件判断中的抢占计数如果PREEMPT_ACTIVE置位对应场景是当前进程被抢占了,此时不再从就绪队列中摘除当前进程,直接调度当前进程释放CPU,以加快选择下一个进程。
II. 哪些场景调用schedule进行调度呢?即调度时机有哪些?汇总如下,后面针对每种场景结合代码分析。
1. 中断,异常
2. 进程退出,睡眠
3. 进程创建,被唤醒,优先级改变时
4. 进程阻塞,如阻塞在信号量,互斥锁
其中2是进程主动调度,其他是被动调度。
III. 概念辨析
1. 禁止抢占就是禁止调度吗?
看到许多资料提到禁止抢占就意味着禁止调度,何解?首先禁止抢占是针对进程(线程)来说的,所以标题的意思是禁止本进程的抢占意味着本进程不可调度了,和内核抢占配置CONFIG_PREEMPT宏不要混淆,配置宏打开意思是内核态可以抢占,是对所有进程而言的。进程A切换到进程B,有两种情况,一 A主动出让CPU 称作yielding,二 A被调度出让CPU称作preemption,两种都是调度,如果禁止抢占,即是禁止了preemption调度。
参考:http://blog.csdn.net/su_linux/article/details/15500053
http://blog.csdn.net/gatieme/article/details/51872618