您的位置:首页 > 其它

scheduler_tick函数详解

2010-06-04 15:25 351 查看
当每次时钟节拍到来时,时钟中断处理函数 timer_interrupt会调用do_timer_interrupt_hook,从而调用do_timer和update_process_times函数,update_process_times则就是用来更新进程使用到的一些跟时间相关的字段,其最重要的是调用scheduler_tick()更新时间片剩余节拍数:
void scheduler_tick(void)
{

int cpu = smp_processor_id();
runqueue_t *rq = this_rq();
task_t *p = current;
把转换为纳秒的TSC的当前值,也就是读取处理器中,存放记录着当前距离开机后时间戳寄存器的值,存入本地运行队列的timestamp_last_tick字段。我们知道在intel奔腾架构的CPU中有个TSC(时间戳寄存器),当系统的时钟节拍为1GHz时,那么时间戳每纳秒增加1次。这个时间戳是从函数sched_clock()获得的。

unsigned long long now = sched_clock();
update_cpu_clock(p, rq, now);

rq->timestamp_last_tick = now;

检查当前进程是否是本地CPU的swapper进程(通过p == rq->idle语句判断)。
1、如果本地运行队列除了swapper进程外,还包括另外一个可运行的进程,就设置当前进程的TIF_NEED_RESCHED字段,以强迫进行重新调度。如果内核支持超线程技术,那么,只要一个逻辑CPU运行队列中的所有进程都有比另一个逻辑CPU(两个逻辑CPU对应同一个物理CPU)上已经在执行的进程有低得多的优先级,前一个逻辑CPU就可能空闲,即使它的运行队列中有可运行的进程。
2、没有必要更新swapper进程的时间片计数器,go out

if (p == rq->idle) {
if (wake_priority_sleeper(rq))
goto out;
rebalance_tick(cpu, rq, SCHED_IDLE);
return;
}

检查current->array是否指向本地运行队列的活动链表(通过p->array != rq->active语句判断)。如果不是,说明该进程已经过期,已经处于rq->expired链表中,但还没有被替换,则设置他的TIF_NEED_RESCHED字段,以强迫进行重新调度并go out。
if (p->array != rq->active) {
set_tsk_need_resched(p);
goto out;
}
spin_lock(&rq->lock);
递减当前进程的时间片计数器,并检查是否已经用完时间片(!--p->time_slice)。
if (rt_task(p)) {
如果当前进程是先进先出(FIFO)的实时进程,即在代码中,不进入最后两个if条件断,则函数scheduler_tick()什么都不做。实际上在这种情况下,current所表示的当前进程想占用CPU多久就占用多久,而且不可能比其他优先级低或其他优先级相等的进程所抢占,因此,维持当前进程的最新时间片计数器是没有意义的。
基于时间片轮转的实时进程(rt_task(p) && (p->policy == SCHED_RR))不能更新其动态优先级,而只能修改其时间片。那么,如果current表示基于时间片轮转的实时进程(通过(p->policy == SCHED_RR) && !--p->time_slice语句判断),scheduler_tick()就递减它的时间片计数器并检查时间片是否被用完:
if ((p->policy == SCHED_RR) && !--p->time_slice) {
p->time_slice = task_timeslice(p); 重填进程的时间片计数器
p->first_time_slice = 0; first_time_slice字段被清零:该标志被fork系统调用例程中的copy_process()设置,并在进程的第一个时间片刚用完时立刻清零。
set_tsk_need_resched(p);设置当前进程需要调度的标志,在退出中断时会调用schedule()函数

时间片用完的SCHED_RR进程,重新赋予时间片后放到所在优先级队列的末尾
requeue_task(p, rq->active);
}
goto out_unlock;
}
if (!--p->time_slice) {当前进程是普通进程,递减时间片计数器后,若时间片用完
dequeue_task(p, rq->active);进程从活跃的进程队列中分离
set_tsk_need_resched(p);设置调度标志
p->prio = effective_prio(p);重新计算优先级
p->time_slice = task_timeslice(p);根据新优先级分配时间片
p->first_time_slice = 0;

本地运行队列数据结构中的expired_timestamp字段等于0(即过期进程集合为空),就把当前时钟节拍值赋给expired_timestamp
if (!rq->expired_timestamp)
rq->expired_timestamp = jiffies;
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 {时间片没有用完(current->time_slice不等于0),检查当前进程的剩余时间片是否太长。宏TIMESLICE_GRANULARITY产生两个数的乘积给当前进程的bonus,其中一个数为系统中的CPU数量,另一个为成比例的常量。基本上,具有高静态优先级的交互式进程,其时间片被分成大小为TIMESLICE_GRANULARITY的几个片段,以使这些进程不会独占CPU。
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, rq, NOT_IDLE);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: