Chinaunix首页 | 论坛 | 博客
  • 博客访问: 491959
  • 博文数量: 72
  • 博客积分: 1851
  • 博客等级: 上尉
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 17:50
文章分类

全部博文(72)

文章存档

2013年(1)

2012年(17)

2011年(51)

2010年(3)

分类: LINUX

2011-09-20 09:16:20

一个新进程在创建时,调用sched_fork来设置调度信息,其中关于时间片问题调用如下函数:

sched_fork(struct task_struct *p,int clone_flags)

---------->set_task_cpu(p,cpu)

  1. void set_task_cpu(struct task_struct *p,unsigned int new_cpu)
  2. {
  3.     int old_cpu =task_cpu(p);//父进程所在的cpu
  4.     struct rq *old_rq = cpu_rq(old_cpu),*new_rq = cpu_rq(new_cpu);
  5.     struct cfs_rq *old_cfsrq = task_cfs_rq(p),//获取负载均衡之前的运行队列
  6.              //负载均衡之后的运行队列
  7.              *new_cfsrq = cpu_cfs_rq(old_cfsrq,new_cpu);
  8.     u64 clock_offset;
  9.     clock_offset = old_rq->clock – new_rq->clock;
  10.     ….
  11.     //新进程获取的运行时间(需要测试),没有看到其初始化过程
  12.     p->se.runtime -= old_cfsrq ->min_vruntime – new_cfsrq->min_vruntime;
  13.     __set_task_    cpu(p,new_cpu);    
  14. }

当一个新进程运行时,调用函数wake_up_new_task:

  1. void fastcall wake_up_new_task(struct task_struct *p,
  2.                         unsigned long clone_flags)
  3. {
  4.     unsigned long flags;
  5.     struct rq *rq;
  6.     rq = task_rq_lock(p,&flags);
  7.     BUG_ON(p->state != TASK_RUNNING);    
  8.     update_rq_lock(rq);
  9.     p->prio = effective_prio(p);
  10.     //将任务加入运行队列中
  11.     if(!p->sched_class->task_new || !current->se.on_rq) {
  12.         activate_task(rq,p,0);
  13.     } else {
  14.         p->sched_class->task_new(rq,p);
  15.         inc_nr_running(p,rq);
  16.     }
  17.     check_preempt_curr(rq,p);
  18.     task_rq_unlock(rq,&flags);
  19. }

如果是cfs调度器,就会运行下面的程序:

  1. static void task_new_fair(struct rq *rq,struct task_struct *p)
  2. {
  3.     struct cfs_rq *rq = task_cfs_rq(p);
  4.     struct sched_entity *se = &p->se,*curr =cfs_rq->curr;
  5.     int this_cpu = smp_processor_id();
  6.     sched_info_queued(p);
  7.     update_curr(cfs_rq);
  8.     place_entity(cfs_rq,se,1);//初始化该进程的时间片
  9.     //让子进程在父进程之前运行,最小的运行时间,则放到红黑树的左边
  10.     if(sysctl_sched_child_runs_first && this_cpu == task_cpu(p) &&
  11.             curr && curr->vruntime < se->vruntime) {
  12.             swap(curr->vruntime,se->vruntime);
  13.     }
  14.     enqueue_task_fair(rq,p,0);//放入运行队列
  15.     resched_task(rq->curr);//设置该运行队列中当前任务可调度,让子进程运行
  16. }

  1. static void place_entity(struct cfs_rq *cfs_rq,struct sched_entity *se,int initial)
  2. {
  3.     u64 vruntime;
  4.     vruntime = cfs_rq ->min_vruntime;
  5.     if(sched_feat(TREE_AVG)) { //类似调度特征,静态设置,不清楚具体含义,默认为假
  6.         //选择最右端的进程(时间片最多的进程)
  7.         struct sched_entity *last = __pick_last_entity(cfs_rq);
  8.         if(last) {
  9.             vruntime += last_vruntime;
  10.             vruntime >>=1;
  11.         }
  12.     } else if(sched_feat(APPRO_AVG) && cfs_rq->nr_running)//默认为假
  13.         vruntime += sched_vslice(cfs_rq)/2;//根据当前运行队列的权重计算时间片
  14.     if(initial && sched_feat(START_DEBIT))
  15.         vruntime += sched_vslice_add(cfs_rq,se);//将新进程的权重加到队列中去,之后再计算时间片,保证其先运行
  16.     if(!initial) { //当将任务放入运行队列时
  17.         if(sched_feat(NEW_FAIR_SLEEPERS))
  18.             vruntime -= sysctl_sched_latency;//如果睡眠,则除去调度延迟
  19.             vruntime = max_vruntime(se->vruntime,vruntime);//得到最大时间
  20.     }
  21.     se->vruntime = vruntime;//真正的新进程时间片
  22. }

通过权重计算运行时间片:

  1. //vruntime += sched_vslice(cfs_rq)/2;
  2. sched_vslice(cfs_rq)
  3.     ----->__sched_vslice(cfs_rq->load.weight,cfs_rq->nr_running);
  4. static u64 __sched_vslice(unsigned long rq_weight,unsigned long nr_running)
  5. {
  6.     u64 vslice = __sched_period(nr_running);
  7.     //NICE_0_LOAD-->SCHED_LOAD_SCALE-->1L<<10:1024
  8.     vslice *= NICE_0_LOAD;
  9.     //根据权重来计算每个进程应该运行的时间,权重越大,优先级越高,时间片越小,则运行的机会就越多,这里权重根本与进程无关,至取决于进程所在的运行队列
  10.     do_div(vslice,rq_weight);//vslice = vslice/rq_weight;
  11.     return vslice;
  12. }
  13. //决定一个延迟周期的时间,也就是所有进程都运行一次的总时间
  14. static u64 __sched_period(unsigned long nr_running)
  15. {
  16.     //sysctl_sched_latency:每个进程应该运行一次的间隔时间,默认为20ms
  17.     u64 period = sysctl_sched_latency;
  18.     //sched_nr_latency:在该间隔时间内运行的进程个数,默认为5
  19.     usigned long nr_latency = sched_nr_latency;
  20.     //一个延迟周期的总时间为:sysctl_sched_latency*nr_running/sched_nr_latency
  21.     if(unlikely(nr_running > nr_latency)) {
  22.         period *= nr_running;
  23.         do_div(period,nr_latency);
  24.     }
  25.     return period;
  26. }

系统中,通过active_taskenqueue_task进行封装:

  1. static void activate_task(struct rq *rq,struct task_struct *p,int wakeup)
  2. {
  3.     if(p->state == TASK_UNINTERRUPTIBLE)
  4.         rq->nr_uninterruptible--;
  5.     enqueue_task(rq,p,wakeup);
  6.     inc_nr_running(p,rq);
  7. }
  8. static void enqueue_task(struct rq *rq,struct task_struct *p ,int wakeup)
  9. {
  10.     sched_info_queued(p);
  11.     p->sched_class->enqueue_task(rq,p,wakeup);
  12.     p->se.on_rq = 1;
  13. }

参考资料:

linux-2.6.24-3内核源码

professional linux kernel architecture

源码交叉引用:



阅读(1682) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~