关闭

CFS进程调度

标签: structclassoptimizationpatcheachup
643人阅读 评论(0) 收藏 举报
分类:
  1. 一、概述  
  2. linux 2.6.23中采用了一个全新的调度策略CFS(Completely Fair Scheduler)来处理非实时进程。  
  3.   
  4. 二、主要数据结构  
  5. 1.为了和原先的实时策略更好的融合,linux在实现CFS之余,还将内核的调度策略模块化,添加了新的结构体sched_class用于管理不同的调度器。  
  6.   
  7. 2.CFS没有用传统的调度器中时间片的概念,而是使用了新的结构体sched_entity来跟踪一个进程的运行时间。  
  8.     se也可表示组调度,在此不做分析,所以这里se代表一个进程。  
  9. struct sched_entity {  
  10.     struct load_weight  load;       /* for load-balancing */    //se的权重  
  11.     struct rb_node      run_node;               //在红黑树上的节点  
  12.     unsigned int        on_rq;                      //该se是否在rq上  
  13.   
  14.     u64         exec_start;                 //当前cfs_rq的时间,用于计算时间差  
  15.     u64         sum_exec_runtime;           //进程总共运行的时间,real-run time  
  16.     u64         vruntime;                       //进程的virtual-run time  
  17.     u64         prev_sum_exec_runtime;      //进程在醒来的时间  
  18.   
  19.     u64         nr_migrations;  
  20. };  
  21.   
  22. 3.rq  
  23.   
  24. 三、tick中断处理  
  25. 每一次进入tick中断后,会进入scheduler_tick函数来进行scheduler相关的处理:  
  26. void scheduler_tick(void)  
  27. {  
  28.     int cpu = smp_processor_id();  
  29.     struct rq *rq = cpu_rq(cpu);  
  30.     struct task_struct *curr = rq->curr;  
  31.   
  32.     raw_spin_lock(&rq->lock);  
  33.     /* 
  34.         更新运行队列的时间,rq->clock和rq->clock_task,可以看出在真正计算 
  35.         时间的时候用的是clock_task   
  36.     */  
  37.     update_rq_clock(rq);      
  38.   
  39.     curr->sched_class->task_tick(rq, curr, 0);  
  40.     raw_spin_unlock(&rq->lock);  
  41.   
  42.     perf_event_task_tick();  
  43. }  
  44.   
  45. 由于添加了模块化的架构,如果采用的是CFS,将会跳转到函数task_tick_fair进而到函数entity_tick。  
  46. 这个函数的作用是更新进程的时间数据,在判断是否被其他进程抢占。  
  47. static void  
  48. entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)  
  49. {  
  50.     /* 
  51.      * Update run-time statistics of the 'current'. 
  52.      */  
  53.     update_curr(cfs_rq);  
  54.   
  55.     if (cfs_rq->nr_running > 1)  
  56.         check_preempt_tick(cfs_rq, curr);  
  57. }  
  58.   
  59. 正如函数注释所说,update_curr用来更新run-time的统计数据,系统会根据这些数据最终决定调度哪个进程。  
  60. static void update_curr(struct cfs_rq *cfs_rq)  
  61. {  
  62.     struct sched_entity *curr = cfs_rq->curr;  
  63.     u64 now = rq_of(cfs_rq)->clock_task;  
  64.     unsigned long delta_exec;  
  65.   
  66.     if (unlikely(!curr))  
  67.         return;  
  68.   
  69.     /* 
  70.      * Get the amount of time the current task was running 
  71.      * since the last time we changed load (this cannot 
  72.      * overflow on 32 bits): 
  73.      */  
  74.     /* 
  75.         delta_exec为自从上次变动以来的时间。 
  76.     */  
  77.     delta_exec = (unsigned long)(now - curr->exec_start);  
  78.     if (!delta_exec)  
  79.         return;  
  80.   
  81.     __update_curr(cfs_rq, curr, delta_exec);  
  82.     curr->exec_start = now;  
  83. }  
  84.   
  85. /* 
  86.  * Update the current task's runtime statistics. Skip current tasks that 
  87.  * are not in our scheduling class. 
  88.  */  
  89. static inline void  
  90. __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,  
  91.           unsigned long delta_exec)  
  92. {  
  93.     unsigned long delta_exec_weighted;  
  94.   
  95.     //更新进程的真实运行时间  
  96.     curr->sum_exec_runtime += delta_exec;  
  97.   
  98.     /*  calc_delta_fair用来将真实时间转化为虚拟时间。 
  99.         进程的优先级不同,它在系统中的地位(也就是权重)也不同 
  100.         进程的优先级越高,它的虚拟时间走的越慢。 
  101.     */  
  102.     delta_exec_weighted = calc_delta_fair(delta_exec, curr);  
  103.   
  104.     //更新进程的虚拟运行时间  
  105.     curr->vruntime += delta_exec_weighted;  
  106.     update_min_vruntime(cfs_rq);  
  107. }  
  108.   
  109. 每个进程在其产生(fork)的时候,都会根据其父亲的优先级产生它的优先级和权重(sched_fork函数)。  
  110. /* 
  111.  * delta /= w 
  112.  */  
  113. static inline unsigned long  
  114. calc_delta_fair(unsigned long delta, struct sched_entity *se)  
  115. {  
  116.     //如果该进程拥有nice为0的权重,这是他的虚拟时钟和真实时钟是一样速度的。  
  117.     if (unlikely(se->load.weight != NICE_0_LOAD))  
  118.         delta = calc_delta_mine(delta, NICE_0_LOAD, &se->load);  
  119.   
  120.     return delta;  
  121. }  
  122.   
  123. 从注释来看calc_delta_mine的计算公式为delta *= weight / lw,也就是说进程的权重越大,时钟走的越慢,而且是线性的。  
  124.   
  125. min_vruntime是cfs的rq中的一个成员,是cfs时间的基准,在cfs中起这至关重要的作用。  
  126. 自cfs产生以来,这部分的代码改动也是很频繁的。  
  127. static void update_min_vruntime(struct cfs_rq *cfs_rq)  
  128. {  
  129.     u64 vruntime = cfs_rq->min_vruntime;  
  130.   
  131.     /* 
  132.         由于当前运行的进程是不在红黑树上的,所以关于cfs_rq->min_vruntime的更新 
  133.         必须要考虑当前的进程,以免产生不公平,这是以前的调度器所疏忽的。 
  134.         如果有当前进程,就以当前进程作为基准计算 
  135.     */  
  136.     if (cfs_rq->curr)  
  137.         vruntime = cfs_rq->curr->vruntime;  
  138.   
  139.     if (cfs_rq->rb_leftmost) {  
  140.         struct sched_entity *se = rb_entry(cfs_rq->rb_leftmost,  
  141.                            struct sched_entity,  
  142.                            run_node);  
  143.   
  144.         if (!cfs_rq->curr)  
  145.             /* 
  146.                 如果没有当前进程,这个在什么时候出现? 
  147.                 其他策略的进程在运行时? 
  148.                 就不用考虑它了,就是最小的运行时间 
  149.             */  
  150.             vruntime = se->vruntime;  
  151.         else  
  152.             /* 
  153.                 如果有当前进程,还要考虑这个最小的运行时间 
  154.             */  
  155.             vruntime = min_vruntime(vruntime, se->vruntime);  
  156.     }  
  157.   
  158.     //最后,更新cfs_rq->min_vruntime,这个值是单调增加的。  
  159.     cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);  
  160. }  
  161.   
  162. 分析完update_curr函数,我们回到entity_tick函数,接着往下看。下面两行的作用是判断当前的进程是否需要被抢占。能够发生抢占首要条件是nr_running大于1。  
  163. /* 
  164.  * Preempt the current task with a newly woken task if needed: 
  165.  */  
  166. static void  
  167. check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)  
  168. {  
  169.     unsigned long ideal_runtime, delta_exec;  
  170.     struct sched_entity *se;  
  171.     s64 delta;  
  172.   
  173.     //计算curr进程的理想运行时间  
  174.     ideal_runtime = sched_slice(cfs_rq, curr);  
  175.     //计算该进程实际运行时间  
  176.     delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;  
  177.     //如果实际运行的时间超出了它应该运行的时间,则set该进程的TIF_NEED_RESCHED标志位  
  178.     if (delta_exec > ideal_runtime) {  
  179.         resched_task(rq_of(cfs_rq)->curr);  
  180.         //如注释,如果当前进程运行了足够时间,就取消它在buddy中的优先权  
  181.         /* 
  182.          * The current task ran long enough, ensure it doesn't get 
  183.          * re-elected due to buddy favours. 
  184.          */  
  185.         clear_buddies(cfs_rq, curr);  
  186.         return;  
  187.     }  
  188.   
  189.     /* 
  190.         这里是第二个抢占条件:最小虚拟运行时间的进程和当前进程的虚拟运行时间进行比较, 
  191.         如果后者比前者大了 ideal_runtime,就需要进行调度。 
  192.         为什么要用虚拟时间个sched_slice产生的真实时间进行比较呢? 
  193.          大了ideal_runtime又能代表什么呢? 
  194.     */  
  195.     /* 
  196.      * Ensure that a task that missed wakeup preemption by a 
  197.      * narrow margin doesn't have to wait for a full slice. 
  198.      * This also mitigates buddy induced latencies under load. 
  199.      */  
  200.     if (delta_exec < sysctl_sched_min_granularity)  
  201.         return;  
  202.   
  203.     se = __pick_first_entity(cfs_rq);  
  204.     delta = curr->vruntime - se->vruntime;  
  205.   
  206.     if (delta < 0)  
  207.         return;  
  208.   
  209.     if (delta > ideal_runtime)  
  210.         resched_task(rq_of(cfs_rq)->curr);  
  211. }  
  212.   
  213. sched_slice函数用来计算一个进程时间基准(wall time),一个进程的理论运行时间是和整个cfs_rq中的进程数量和权重有关系的。  
  214. 不考虑调度组的话,函数等价于:  
  215. /* 
  216.  * We calculate the wall-time slice from the period by taking a part 
  217.  * proportional to the weight. 
  218.  * 
  219.  * s = p*P[w/rw] 
  220.  */  
  221. static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)  
  222. {  
  223.     /* 
  224.         __sched_period这个函数得到的是每一个进程运行一次的时间总和,公式为: 
  225.         p = (nr <= nl) ? l : mg * nr 
  226.         l   :系统常数,为调度延时,就是系统所有进程运行一次的时间总和 
  227.         nl  :系统常数,系统活动进程的上限 
  228.         nr  :当前的进程数 
  229.         mg  :系统常数,最小的调度时间间隔 
  230.     */  
  231.     u64 slice = __sched_period(cfs_rq->nr_running + !se->on_rq);  
  232.     struct load_weight *load;  
  233.     struct load_weight lw;  
  234.   
  235.     load = &cfs_rq->load;  
  236.   
  237.     //这里和上面都考虑了情况,当进程刚刚创建时,当前进程不在运行队列中,为了计算合理,就临时加上去。  
  238.     if (unlikely(!se->on_rq)) {  
  239.         lw = cfs_rq->load;  
  240.   
  241.         update_load_add(&lw, se->load.weight);  
  242.         load = &lw;  
  243.     }  
  244.     //根据权重得到属于自己的那份时间  
  245.     slice = calc_delta_mine(slice, se->load.weight, load);  
  246.   
  247.     return slice;  
  248. }  
  249.   
  250. 四、新进程中的调度  
  251. 1.sched_fork出现在copy_process中。  
  252. /* 
  253.  * fork()/clone()-time setup: 
  254.  */  
  255. void sched_fork(struct task_struct *p)  
  256. {  
  257.     unsigned long flags;  
  258.     int cpu = get_cpu();  
  259.   
  260.     //初始化task_struct中调度器相关的成员。  
  261.     __sched_fork(p);  
  262.     /* 
  263.      * We mark the process as running here. This guarantees that 
  264.      * nobody will actually run it, and a signal or other external 
  265.      * event cannot wake it up and insert it on the runqueue either. 
  266.      */  
  267.     p->state = TASK_RUNNING;  
  268.   
  269.     //确保临时的优先级的提升不会继承到新的进程中  
  270.     /* 
  271.      * Make sure we do not leak PI boosting priority to the child. 
  272.      */  
  273.     p->prio = current->normal_prio;  
  274.   
  275.     //如果设置了sched_reset_on_fork,会恢复默认调度策略  
  276.     /* 
  277.      * Revert to default priority/policy on fork if requested. 
  278.      */  
  279.     if (unlikely(p->sched_reset_on_fork)) {  
  280.         if (task_has_rt_policy(p)) {  
  281.             p->policy = SCHED_NORMAL;  
  282.             p->static_prio = NICE_TO_PRIO(0);  
  283.             p->rt_priority = 0;  
  284.         } else if (PRIO_TO_NICE(p->static_prio) < 0)  
  285.             p->static_prio = NICE_TO_PRIO(0);  
  286.   
  287.         p->prio = p->normal_prio = __normal_prio(p);  
  288.         //根据优先级和调度策略设置权重  
  289.         set_load_weight(p);  
  290.   
  291.         /* 
  292.          * We don't need the reset flag anymore after the fork. It has 
  293.          * fulfilled its duty: 
  294.          */  
  295.         p->sched_reset_on_fork = 0;  
  296.     }  
  297.   
  298.     //如果不是实时进程,就用CFS调度  
  299.     if (!rt_prio(p->prio))  
  300.         p->sched_class = &fair_sched_class;  
  301.   
  302.     if (p->sched_class->task_fork)  
  303.         p->sched_class->task_fork(p);  
  304.   
  305.     .......  
  306. }  
  307.   
  308. 进入CFS中的task_fork_fair函数。  
  309. /* 
  310.  * called on fork with the child task as argument from the parent's context 
  311.  *  - child not yet on the tasklist 
  312.  *  - preemption disabled 
  313.  */  
  314. static void task_fork_fair(struct task_struct *p)  
  315. {  
  316.     struct cfs_rq *cfs_rq = task_cfs_rq(current);  
  317.     struct sched_entity *se = &p->se, *curr = cfs_rq->curr;  
  318.     int this_cpu = smp_processor_id();  
  319.     struct rq *rq = this_rq();  
  320.     unsigned long flags;  
  321.   
  322.     raw_spin_lock_irqsave(&rq->lock, flags);  
  323.   
  324.     //更新rq的时钟  
  325.     update_rq_clock(rq);  
  326.   
  327.     //更新cfs_rq的统计数据  
  328.     update_curr(cfs_rq);  
  329.   
  330.     //子进程虚拟时间以 父进程的虚拟时间为基准  
  331.     if (curr)  
  332.         se->vruntime = curr->vruntime;  
  333.     //调整子进程虚拟时间  
  334.     place_entity(cfs_rq, se, 1);  
  335.   
  336.     /* 
  337.         参数sysctl_sched_child_runs_first强制子进程在父进程之前运行。 
  338.         entity_before(curr, se)判断是否需要进行虚拟运行时间的对调, 
  339.         只有父进程的虚拟运行时间小于子进程的虚拟运行时间时才需要此操作。 
  340.     */  
  341.     if (sysctl_sched_child_runs_first && curr && entity_before(curr, se)) {  
  342.         /* 
  343.          * Upon rescheduling, sched_class::put_prev_task() will place 
  344.          * 'current' within the tree based on its new key value. 
  345.          */  
  346.         swap(curr->vruntime, se->vruntime);  
  347.         resched_task(rq->curr);  
  348.     }  
  349.   
  350.     /* 
  351.         将vruntime减去一个cfs_rq->min_vruntime,使得vruntime变成一个相对的时间, 
  352.         这样做好像是为了在SMP的情况下有一个标准的接口,这个我还不太清楚, 
  353.         有机会再研究 
  354.     */  
  355.     se->vruntime -= cfs_rq->min_vruntime;  
  356.   
  357.     raw_spin_unlock_irqrestore(&rq->lock, flags);  
  358. }  
  359.   
  360. static void  
  361. place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)  
  362. {  
  363.     //基准为min_vruntime  
  364.     u64 vruntime = cfs_rq->min_vruntime;  
  365.   
  366.     /* 
  367.      * The 'current' period is already promised to the current tasks, 
  368.      * however the extra weight of the new task will slow them down a 
  369.      * little, place the new task so that it fits in the slot that 
  370.      * stays open at the end. 
  371.      */  
  372.     /* 
  373.         新进程开始的时候,基准值要比min_vruntime稍微慢一些。 
  374.     */  
  375.     if (initial)  
  376.         vruntime += sched_vslice(cfs_rq, se);  
  377.   
  378.     /* sleeps up to a single latency don't count. */  
  379.     if (!initial) {  
  380.         /* 
  381.             这里是睡眠进程唤醒时的时间处理。 
  382.             因为进程休眠了,se->vruntime肯定很小,但是如果太小 
  383.             的话,又会抢其他进程运行的机会。所以这里采用了 
  384.             cfs_rq->min_vruntime - thresh。即保证了它调度的优先权, 
  385.             又不至于太小,影响其他进程。 
  386.         */  
  387.         unsigned long thresh = sysctl_sched_latency;  
  388.   
  389.         /* 
  390.          * Halve their sleep time's effect, to allow 
  391.          * for a gentler effect of sleepers: 
  392.          */  
  393.         if (sched_feat(GENTLE_FAIR_SLEEPERS))  
  394.             thresh >>= 1;  
  395.   
  396.         vruntime -= thresh;  
  397.     }  
  398.   
  399.     //这里保证vruntime只能往大了调整  
  400.     /* ensure we never gain time by being placed backwards. */  
  401.     vruntime = max_vruntime(se->vruntime, vruntime);  
  402.   
  403.     se->vruntime = vruntime;  
  404. }  
  405.   
  406. 2.创建新的进程的另一个调度相关的函数是wake_up_new_task,作用见下面注释。  
  407. /* 
  408.  * wake_up_new_task - wake up a newly created task for the first time. 
  409.  * 
  410.  * This function will do some initial scheduler statistics housekeeping 
  411.  * that must be done for every newly created context, then puts the task 
  412.  * on the runqueue and wakes it. 
  413.  */  
  414. void wake_up_new_task(struct task_struct *p)  
  415. {  
  416.     unsigned long flags;  
  417.     struct rq *rq;  
  418.   
  419.     raw_spin_lock_irqsave(&p->pi_lock, flags);  
  420.   
  421.     rq = __task_rq_lock(p);  
  422.     //添加进程到运行队列中  
  423.     activate_task(rq, p, 0);  
  424.     p->on_rq = 1;  
  425.   
  426.     //判断新的进程是否能抢占当前进程  
  427.     check_preempt_curr(rq, p, WF_FORK);  
  428.   
  429.     task_rq_unlock(rq, p, &flags);  
  430. }  
  431.   
  432. activate_task最终会调到CFS中的函数enqueue_task_fair  
  433. /* 
  434.  * The enqueue_task method is called before nr_running is 
  435.  * increased. Here we update the fair scheduling stats and 
  436.  * then put the task into the rbtree: 
  437.  */  
  438. static void  
  439. enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)  
  440. {  
  441.     struct cfs_rq *cfs_rq;  
  442.     struct sched_entity *se = &p->se;  
  443.   
  444.     //如果已经在运行队列中,什么也不做就返回  
  445.     if (se->on_rq)  
  446.         return;  
  447.   
  448.     enqueue_entity(cfs_rq, se, flags);  
  449.   
  450.     cfs_rq->h_nr_running++;  
  451. }  
  452.   
  453. static void  
  454. enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)  
  455. {  
  456.     /* 
  457.      * Update the normalized vruntime before updating min_vruntime 
  458.      * through callig update_curr(). 
  459.      */  
  460.     /* 
  461.         如果是新创建的进程,可以看到前面减去了一个cfs_rq->min_vruntime, 
  462.         这里再加回来。 
  463.     */  
  464.     if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))  
  465.         se->vruntime += cfs_rq->min_vruntime;  
  466.   
  467.     /* 
  468.      * Update run-time statistics of the 'current'. 
  469.      */  
  470.     update_curr(cfs_rq);  
  471.   
  472.     if (flags & ENQUEUE_WAKEUP) {  
  473.         //如果是进程唤醒时的操作(try_to_wake_up),就会进这里  
  474.         place_entity(cfs_rq, se, 0);  
  475.     }  
  476.   
  477.     account_entity_enqueue(cfs_rq, se);  
  478.   
  479.     //添加进程到红黑树  
  480.     if (se != cfs_rq->curr)  
  481.         __enqueue_entity(cfs_rq, se);  
  482.     se->on_rq = 1;  
  483. }  
  484.   
  485. static void  
  486. account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se)  
  487. {  
  488.     //添加进程的权重到cfs的权重中  
  489.     update_load_add(&cfs_rq->load, se->load.weight);  
  490.     //添加进程的权重到rq的权重中  
  491.     inc_cpu_load(rq_of(cfs_rq), se->load.weight);  
  492.   
  493.     //cfs_rq的进程数量加一  
  494.     cfs_rq->nr_running++;  
  495. }  
  496.   
  497. static void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)  
  498. {  
  499.     const struct sched_class *class;  
  500.   
  501.     if (p->sched_class == rq->curr->sched_class) {  
  502.         rq->curr->sched_class->check_preempt_curr(rq, p, flags);  
  503.     } else {  
  504.         for_each_class(class) {  
  505.             if (class == rq->curr->sched_class)  
  506.                 break;  
  507.             if (class == p->sched_class) {  
  508.                 resched_task(rq->curr);  
  509.                 break;  
  510.             }  
  511.         }  
  512.     }  
  513.   
  514.     /* 
  515.      * A queue event has occurred, and we're going to schedule.  In 
  516.      * this case, we can save a useless back to back clock update. 
  517.      */  
  518.     if (rq->curr->on_rq && test_tsk_need_resched(rq->curr))  
  519.         rq->skip_clock_update = 1;  
  520. }  
  521.   
  522. 判断当前进程是否能被进程p抢占  
  523. /* 
  524.  * Preempt the current task with a newly woken task if needed: 
  525.  */  
  526. static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)  
  527. {  
  528.     struct task_struct *curr = rq->curr;  
  529.     struct sched_entity *se = &curr->se, *pse = &p->se;  
  530.     struct cfs_rq *cfs_rq = task_cfs_rq(curr);  
  531.     int scale = cfs_rq->nr_running >= sched_nr_latency;  
  532.     int next_buddy_marked = 0;  
  533.   
  534.     / /要唤醒的进程和当前运行的进程是同一个,就返回  
  535.     if (unlikely(se == pse))  
  536.         return;  
  537.   
  538.     /* 
  539.      * We can come here with TIF_NEED_RESCHED already set from new task 
  540.      * wake up path. 
  541.      * 
  542.      * Note: this also catches the edge-case of curr being in a throttled 
  543.      * group (e.g. via set_curr_task), since update_curr() (in the 
  544.      * enqueue of curr) will have resulted in resched being set.  This 
  545.      * prevents us from potentially nominating it as a false LAST_BUDDY 
  546.      * below. 
  547.      */  
  548.     //如果调度位已经设置,就不再往下在走了  
  549.     if (test_tsk_need_resched(curr))  
  550.         return;  
  551.   
  552.     /* Idle tasks are by definition preempted by non-idle tasks. */  
  553.     //下面这种情况是一定需要抢占的。  
  554.     if (unlikely(curr->policy == SCHED_IDLE) &&  
  555.         likely(p->policy != SCHED_IDLE))  
  556.         goto preempt;  
  557.   
  558.     /* 
  559.      * Batch and idle tasks do not preempt non-idle tasks (their preemption 
  560.      * is driven by the tick): 
  561.      */  
  562.     //如果不是使用CFS,就返回  
  563.     if (unlikely(p->policy != SCHED_NORMAL))  
  564.         return;  
  565.   
  566.     update_curr(cfs_rq_of(se));  
  567.   
  568.     //判断是否需要抢占的核心函数  
  569.     if (wakeup_preempt_entity(se, pse) == 1) {  
  570.         /* 
  571.          * Bias pick_next to pick the sched entity that is 
  572.          * triggering this preemption. 
  573.          */  
  574.         //last指向最后一个别调度出去的进程。  
  575.         if (!next_buddy_marked)  
  576.             set_next_buddy(pse);  
  577.         goto preempt;  
  578.     }  
  579.   
  580.     return;  
  581.   
  582. preempt:  
  583.     resched_task(curr);  
  584.     /* 
  585.      * Only set the backward buddy when the current task is still 
  586.      * on the rq. This can happen when a wakeup gets interleaved 
  587.      * with schedule on the ->pre_schedule() or idle_balance() 
  588.      * point, either of which can * drop the rq lock. 
  589.      * 
  590.      * Also, during early boot the idle thread is in the fair class, 
  591.      * for obvious reasons its a bad idea to schedule back to it. 
  592.      */  
  593.     if (unlikely(!se->on_rq || curr == rq->idle))  
  594.         return;  
  595.   
  596.     if (sched_feat(LAST_BUDDY) && scale && entity_is_task(se))  
  597.         set_last_buddy(se);  
  598. }  
  599.   
  600. /* 
  601.  * Should 'se' preempt 'curr'. 
  602.  * 
  603.  *             |s1 
  604.  *        |s2 
  605.  *   |s3 
  606.  *         g 
  607.  *      |<--->|c 
  608.  * 
  609.  *  w(c, s1) = -1 
  610.  *  w(c, s2) =  0 
  611.  *  w(c, s3) =  1 
  612.  * 
  613.  */  
  614. static int  
  615. wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)  
  616. {  
  617.     //计算两个虚拟时间之差  
  618.     s64 gran, vdiff = curr->vruntime - se->vruntime;  
  619.   
  620.     //如果se的虚拟时间比curr还大,说明本该curr执行,无需抢占  
  621.     if (vdiff <= 0)  
  622.         return -1;  
  623.   
  624.     /* 
  625.         gran为需要抢占的时间差,只有两个时间差大于需要抢占的时间差, 
  626.         才需要抢占,这里避免了太频繁的抢占。 
  627.     */  
  628.     gran = wakeup_gran(curr, se);  
  629.     if (vdiff > gran)  
  630.         return 1;  
  631.   
  632.     return 0;  
  633. }  
  634.   
  635. 五、schedule函数  
  636. 1.如果需要调度的话就有将当前进程从运行队列中清出去,这里调用函数deactivate_task,flags为DEQUEUE_SLEEP。  
  637. /* 
  638.  * deactivate_task - remove a task from the runqueue. 
  639.  */  
  640. static void deactivate_task(struct rq *rq, struct task_struct *p, int flags)  
  641. {  
  642.     if (task_contributes_to_load(p))  
  643.         rq->nr_uninterruptible++;  
  644.   
  645.     dequeue_task(rq, p, flags);  
  646. }  
  647.   
  648. static void dequeue_task(struct rq *rq, struct task_struct *p, int flags)  
  649. {  
  650.     //更新rq时间  
  651.     update_rq_clock(rq);  
  652.   
  653.     p->sched_class->dequeue_task(rq, p, flags);  
  654. }  
  655.   
  656. CFS中,调用函数dequeue_task_fair  
  657. /* 
  658.  * The dequeue_task method is called before nr_running is 
  659.  * decreased. We remove the task from the rbtree and 
  660.  * update the fair scheduling stats: 
  661.  */  
  662. static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)  
  663. {  
  664.     struct cfs_rq *cfs_rq;  
  665.     struct sched_entity *se = &p->se;  
  666.     int task_sleep = flags & DEQUEUE_SLEEP;  
  667.   
  668.     cfs_rq = cfs_rq_of(se);  
  669.     dequeue_entity(cfs_rq, se, flags);  
  670.   
  671.     cfs_rq->h_nr_running--;  
  672. }  
  673.   
  674. static void  
  675. dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)  
  676. {  
  677.     /* 
  678.      * Update run-time statistics of the 'current'. 
  679.      */  
  680.     update_curr(cfs_rq);  
  681.   
  682.     update_stats_dequeue(cfs_rq, se);  
  683.     if (flags & DEQUEUE_SLEEP) {  
  684.         //更新一些统计数据  
  685.     }  
  686.   
  687.     //如果进程退出,清掉buddy里的指针  
  688.     clear_buddies(cfs_rq, se);  
  689.   
  690.     if (se != cfs_rq->curr)  
  691.         __dequeue_entity(cfs_rq, se);  
  692.     se->on_rq = 0;  
  693.     //account_entity_enqueue的相反操作  
  694.     account_entity_dequeue(cfs_rq, se);  
  695.   
  696.     /* 
  697.         还是为了SMP 
  698.     */  
  699.     /* 
  700.      * Normalize the entity after updating the min_vruntime because the 
  701.      * update can refer to the ->curr item and we need to reflect this 
  702.      * movement in our normalized position. 
  703.      */  
  704.     if (!(flags & DEQUEUE_SLEEP))  
  705.         se->vruntime -= cfs_rq->min_vruntime;  
  706.   
  707.     update_min_vruntime(cfs_rq);  
  708. }  
  709.   
  710. 2.忽略SMP,第二个操作就是put_prev_task  
  711. static void put_prev_task(struct rq *rq, struct task_struct *prev)  
  712. {  
  713.     if (prev->on_rq || rq->skip_clock_update < 0)  
  714.         update_rq_clock(rq);  
  715.     prev->sched_class->put_prev_task(rq, prev);  
  716. }  
  717.   
  718. /* 
  719.  * Account for a descheduled task: 
  720.  */  
  721. static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)  
  722. {  
  723.     struct sched_entity *se = &prev->se;  
  724.     struct cfs_rq *cfs_rq;  
  725.   
  726.     cfs_rq = cfs_rq_of(se);  
  727.     put_prev_entity(cfs_rq, se);  
  728. }  
  729.   
  730. static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)  
  731. {  
  732.     if (prev->on_rq) {  
  733.         /* 
  734.             如下面解释,被调度的进程有可能还是在运行队列上的,例如在 
  735.             调度的时候收到了一个信号。 
  736.         */  
  737.         /* 
  738.          * If still on the runqueue then deactivate_task() 
  739.          * was not called and update_curr() has to be done: 
  740.          */  
  741.         update_curr(cfs_rq);  
  742.   
  743.         /* Put 'current' back into the tree. */  
  744.         /* 
  745.             正在运行进程,是不在红黑树中的,set_next_entity有这个处理, 
  746.             运行的进程并不在红黑树中移动。