whealth的嵌入式天堂

I loving big BENZ

scheduler_tick函数详解

 

当每次时钟节拍到来时,时钟中断处理函数 timer_interrupt会调用do_timer_interrupt_hook,从而调用do_timerupdate_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;

 

检查当前进程是否是本地CPUswapper进程(通过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);

}

 

 

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭