/*如果当前进程的调度策略是轮转RR,那么需要判断当前进程的时间片是否已经用完,如果已经用完,则重新计算时间片值,然后将该进程挂接到就绪队列run-queue的最后*/
if (unlikely(prev->policy == SCHED_RR))
if (!prev->counter) {
prev->counter = NICE_TO_TICKS(prev->nice);
move_last_runqueue(prev);
}
/*假如进程为TASK_INTERRUPTTIBLE状态,则将其状态置为TASK_RUNNING。如是其它状态,则将该进程转为睡眠状态,从运行队列中删除。(已不具备运行的条件) */
switch (prev->state) {
case TASK_INTERRUPTIBLE:
if (signal_pending(prev)) {
prev->state = TASK_RUNNING;
break;
}
default:
del_from_runqueue(prev);
case TASK_RUNNING:;
}
/*当前进程不需要重新调度*/
prev->need_resched = 0;
/*下面是一般的进程调度过程*/
repeat_schedule:
next = idle_task(this_cpu);
c = -1000;
/*遍历进程就绪队列,如果该进程能够进行调度(对于SMP来说就是判断当前CPU未被占用能够执行这个进程,对于非SMP系统则为1),则计算该进程的优先级,如果优先级大于当前进程,则next指针指向新的进程,循环直到找到优先级最大的那个进程*/
list_for_each(tmp, &runqueue_head) {
p = list_entry(tmp, struct task_struct, run_list);
if (can_schedule(p, this_cpu)) {
int weight = goodness(p, this_cpu, prev->active_mm);
if (weight > c)
c = weight, next = p;
}
}
/* 判断是否需要重新计算每个进程的时间片,判断的依据是所有正准备进行调度的进程时间片耗尽,这时,就需要对就绪队列中的每一个进程都重新计算时间片,然后返回前面的调度过程,重新在就绪队列当中查找优先级最高的进程执行调度。 */
if (unlikely(!c)) {
struct task_struct *p;
release_kernel_lock(prev);
now = sched_clock( );
if (likely(now - prev->timestamp timestamp;
else
run_time = NS_MAX_SLEEP_AVG;
/*
* Tasks with interactive credits get charged less run_time
* at high sleep_avg to delay them losing their interactive
* status
*/
if (HIGH_CREDIT(prev))
run_time /= (CURRENT_BONUS(prev) ? : 1);
spin_lock_irq(&rq->lock);
/*
* if entering off of a kernel preemption go straight
* to picking the next task.
*/
switch_count = &prev->nivcsw;
if (prev->state && !(preempt_count( ) & PREEMPT_ACTIVE)) {
switch_count = &prev->nvcsw;
if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
unlikely(signal_pending(prev))))
prev->state = TASK_RUNNING;
else
deactivate_task(prev, rq);
}
/*如果当前就绪队列中没有正在运行的任务,则进行进程上下文切换,到空闲进程*/
if (unlikely(!rq->nr_running)) {
#ifdef CONFIG_SMP
load_balance(rq, 1, cpu_to_node_mask(smp_processor_id( )));
#endif
if (!rq->nr_running) {
next = rq->idle;
rq->expired_timestamp = 0;
goto switch_tasks;
}
}
/*获取当前活动数组指针,判断处于活动状态的进程数目,如果活动数组当中已经没有任何活动的进程,则需要将活动数组与过期数组进行对换,这样就可以很快的开始新一轮的调度,这里array指针总是指向活动数组。*/
array = rq->active;
if (unlikely(!array->nr_active)) {
/*
* Switch the active and expired arrays.
*/
rq->active = rq->expired;
rq->expired = array;
array = rq->active;
rq->expired_timestamp = 0;
rq->best_expired_prio = MAX_PRIO;
}
/*由于调度器事先将活动数组的索引存放在了一个64位的bitmap位图结构当中,排序的依据是根据每个进程的优先级排列的,所以当我们需要查找当前活动数组中优先级最高的进程的索引时,仅仅需要从位图中找到最前面的一个索引值就可以了,而不需要象2.4内核那样每次遍历就绪队列,计算每个进程的优先级然后才能确定应该选择哪个进程作为下一个调度的对象。
查找到当前队列中优先级最高的进程索引,然后在列表中找到相应的进程。*/
idx = sched_find_first_bit(array->bitmap);
queue = array->queue + idx;
next = list_entry(queue->next, task_t, run_list);
/*一个进程的优先级是从0到MAX_PRIO-1(MAX_PRIO为140),实时进程优先级是从0到MAX_RT_PRIO-1(MAX_RT_PRIO为100),SCHED_NORMAL任务的优先级在MAX_RT_PRIO到MAX_PRIO-1之间,优先级值越小表明优先级越大。
在这里首先判断当前需要调度的进程是否为实时进程,如果不是看是不是处于活动状态,如果是,根据其消耗的时间片,重新计算优先级。*/
if (!rt_task(next) && next->activated > 0) {
unsigned long long delta = now - next->timestamp;
从system_call入口的汇编程序的主要功能是:
l 保存寄存器当前值;
l 检验是否为合法的系统调用;
l 根据系统调用表sys_call_table和EAX持有的系统调用号找出并转入系统调用响应函数;
l 从该响应函数返回后,让EAX寄存器保存函数返回值,跳转至ret_from_sys_call(arch/i386/kernel/entry.S)。
以ret_from_sys_call入口的汇编程序段在Linux进程管理中起着十分重要的作用。所有系统调用结束前,以及大部分中断服务程序返回前,都会跳转至此入口地址。反过来,该段程序也不仅仅为系统调用服务,它还要处理中断嵌套、bottom half队列、CPU调度、信号等事务。