标签:
参考的是ULK第三版,Linux2.6.11.12内核版本。
调度程序依靠几个函数来完成调度工作,其中最重要的第一个函数是scheduler_tick函数。
/** * 维持当前最新的time_slice计数器 * 每次时钟节拍到来时,scheduler_tick函数将被调用,以执行与调度相关的操作。 */ void scheduler_tick(void) { int cpu = smp_processor_id(); runqueue_t *rq = this_rq();//宏this_rq()产生本地CPU运行队列的地址 task_t *p = current; /** * 把转换为纳秒的TSC的当前值存入本地运行队列的timestamp_last_tick中。这个时间戳由sched_clock获得。 */ rq->timestamp_last_tick = sched_clock(); /** * 检查当前进程是否是idle进程。swapper进程,就是0号进程 */ if (p == rq->idle) { /** * 检查运行队列中除了IDLE进程外,是否还有其他可运行进程。 * 如果有,就设置当前进程的TIF_NEED_SCHEDULED字段,以强迫进行调度。 */ if (wake_priority_sleeper(rq)) goto out; rebalance_tick(cpu, rq, SCHED_IDLE); /** * 没有必要更新IDLE进程的时间片计数器,所以此处直接返回。 */ return; } /* Task might have expired already, but not scheduled off yet */ /** * 检查current->array是否指向本地运行队列的活动链表。 * 如果不是,说明进程已经过期但还没有被替换,设置TIF_NEED_SCHEDULED标志,以强制进行重新调度。 */ if (p->array != rq->active) { set_tsk_need_resched(p); goto out; } /** * 获得运行队列的自旋锁。 */ spin_lock(&rq->lock); /* * The task was running during this tick - update the * time slice counter. Note: we do not update a thread‘s * priority until it either goes to sleep or uses up its * timeslice. This makes it possible for interactive tasks * to use up their timeslices at their highest priority levels. */ /** * 递减当前进程的时间片计数器,并检查是否已经用完时间片。 * 由于进程的调度类型不同,函数所执行的操作也有很大差别。 */ if (rt_task(p)) {/* 如果是实时进程,就进一步根据是FIFO还是RR类型的实时进程 */ /* * RR tasks need a special form of timeslice management. * FIFO tasks have no timeslices. */ /** * 对SCHED_RR类型的实时进程,需要递减它的时间片。 * 对SCHED_FIFO类型的实时进程,什么都不做,退出。 */ if ((p->policy == SCHED_RR) && !--p->time_slice) { /** * 对SCHED_RR类型的实时进程,如果它的时间片已经用完,就执行此下动作,以达到抢占当前进程的目的。 * 如果必要的话,就尽快抢占。 */ p->time_slice = task_timeslice(p);/* 重新计算它的时间片,它根据进程的静态优先级来计算它的时间片。 */ /** * 直到这里,说明进程一定不是第一次运行了,它已经用完了一次它的时间片,将first_time_slice置为0. * 这样,它即使退出,也不会将剩余的时间片还给父进程了。 */ p->first_time_slice = 0; /** * 设置调度标志,以达到尽快抢占的目的。 */ set_tsk_need_resched(p); /* put it at the end of the queue: */ /** * 将实时进程放到队列末尾。这样,如此链表中还有其他同优先级的RR进程,其他进程就能够得到运行了。 */ requeue_task(p, rq->active); } goto out_unlock; } /** * 运行到此,说明进程是普通进程。现在开始更新普通进程的时间片。 */ if (!--p->time_slice) {/* 首先递减普通进程的时间片计数器。如果用完,继续执行以下操作 */ /** * 既然用完了,就将当前进程从活动集合中摘除。 */ dequeue_task(p, rq->active); /** * 当然,当前进程既然已经过期,就必须设置重新调度标志,以便在中断返回前调用schedule选择另外一个进程来运行。 */ set_tsk_need_resched(p); /** * 更新当前进程的动态优先级。 * effective_prio根据当前进程的static_prio和sleep_avg字段,计算进程的动态优先级。 */ p->prio = effective_prio(p); /** * 重填进程的时间片 */ p->time_slice = task_timeslice(p); /** * 既然当前进程的一个时间片已经用完,当然就需要清除first_time_slice标志了。 */ p->first_time_slice = 0; /** * 如果本地运行队列的expired_timestamp为0,表示过期进程集合为空。 * 并且当前进程马上就会变成过期进程,那么将当前jiffies赋给expired_timestamp * expired_timestamp表示当前队列中,过期队列中最老进程被插入过期队列的时间。 */ if (!rq->expired_timestamp) rq->expired_timestamp = jiffies; /** * 把当前进程插入过期集合或者活动集合。 * TASK_INTERACTIVE判断当前进程是否是一个交互式进程。 * TASK_INTERACTIVE宏检查运行队列中的第一个过期进程的等待时间是否已经超过1000个时钟节拍乘以运行队列中的可运行进程数+1,如果是返回1. * 如果当前进程的静态优先级大于过期进程的静态优先级,也返回1. */ if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { /** * 当前进程不是交互式进程,或者过期队列中有优先级更高的进程,那么将当前进程插入到过期队列。 */ enqueue_task(p, rq->expired); /** * 如果当前进程是过期队列中优先级最高的低,就更新过期队列的最高优先级。 */ if (p->static_prio < rq->best_expired_prio) rq->best_expired_prio = p->static_prio; } else enqueue_task(p, rq->active);/* 进程是交互式进程,并且比过期队列中所有进程的静态优先级高,那么就将它加到活动队列中。这实际上是对交互式进程的优待。 */ } else {/* 普通进程的时间片还没有用完,需要进一步检查是否时间片太长 */ /* * Prevent a too long timeslice allowing a task to monopolize * the CPU. We do this by splitting up the timeslice into * smaller pieces. * * Note: this does not mean the task‘s timeslices expire or * get lost in any way, they just might be preempted by * another task of equal priority. (one with higher * priority would have preempted this task already.) We * requeue this task to the end of the list on this priority * level, which is in essence a round-robin of tasks with * equal priority. * * This only applies to tasks in the interactive * delta range with at least TIMESLICE_GRANULARITY to requeue. */ /** * 检查当前进程的时间片是否太长,因为对于交互式进程来说,它时间片用完后,可能会再插入到活动队列,可能导致这种进程的时间片特别长。 */ if (TASK_INTERACTIVE(p) && !((task_timeslice(p) - p->time_slice) % TIMESLICE_GRANULARITY(p)) && (p->time_slice >= TIMESLICE_GRANULARITY(p)) && (p->array == rq->active)) { requeue_task(p, rq->active); set_tsk_need_resched(p); } } out_unlock: /** * 释放自旋锁。 */ spin_unlock(&rq->lock); out: /** * 调用rebalance_tick函数,该函数应该保证不同CPU的运行队列包含数量基本相同的可运行进程。 */ rebalance_tick(cpu, rq, NOT_IDLE); }
标签:
原文地址:http://www.cnblogs.com/joey-hua/p/5770730.html