2014年9月14日 星期日

__schedule()


source:__schedule()



2731 static void __sched __schedule(void)
2732 {
2733         struct task_struct *prev, *next;
/*prev表示調度前的task,next表示調度後的task*/
2734         unsigned long *switch_count;
2735         struct rq *rq;
2736         int cpu;
2738   need_resched:
/*kernel即将返回user mode的时候,如果need resched被设置,會導致schedule()被调用,此時就會發生task preempted,通常這個flag會在中斷及system call的返回路徑進行檢查,當從system call或中斷返回和kernel處於插隊狀態時,kernel會預先呼叫這個function來returning*/
2739         preempt_disable();/*用來關閉插隊狀態*/
2740         cpu = smp_processor_id();/*獲得目前CPU的ID*/
2741         rq = cpu_rq(cpu);/*使rq指向CPU對應的runqueue*/
2742         rcu_note_context_switch(cpu);
/*標示當前CPU發生任務切換,會更改全部狀態,並判斷是否最後一個進行contex switch的CPU,如果是,call callbacks*/
2743         prev = rq->curr;/*讓prev為當前task*/
2745         schedule_debug(prev);
/*由於前面有宣告關閉插隊狀態,而又呼叫need_resched()這個function就會出錯。所以用來檢查*/
2747         if (sched_feat(HRTICK))
/*這個macro是用來判斷kernel的scheduler中哪些feature已經開啟了,定義在(Linux/kernel/sched/features.h)*/
2748                 hrtick_clear(rq);/*使用high-resolution timer提供準確的preemption points*/ 
2755         smp_mb__before_spinlock();/*啟動spinlock*/
2756         raw_spin_lock_irq(&rq->lock);
2758         switch_count = &prev->nivcsw;
/*根據prev狀態做相對應處理*/
/*檢查prev狀態。如果不是ready狀態且沒有任何kernel被插隊,應該從runqueue中刪除prev。如果狀態為TASK_INTERRUPTIBLE,則把該task設為TASK_RUNNING。*/
2759         if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
2760                 if (unlikely(signal_pending_state(prev->state, prev))) {
2761                         prev->state = TASK_RUNNING;
/*如果該task為non block且在等待,則會改成TASK_RUNNING狀態並離開RUN QUEUE*/
2762                 } else {
2763                         deactivate_task(rq, prev, DEQUEUE_SLEEP);/*從runqueue中將task刪除*/
2764                         prev->on_rq = 0;
2766                         /*
2767                          * If a worker went to sleep, notify and ask workqueue
2768                          * whether it wants to wake up a task to maintain
2769                          * concurrency.一些並發性判斷和操作,不影響整體流程
2770                          */
2771                         if (prev->flags & PF_WQ_WORKER) {
2772                                 struct task_struct *to_wakeup;
2774                                 to_wakeup = wq_worker_sleeping(prev, cpu);
2775                                 if (to_wakeup)
2776                                         try_to_wake_up_local(to_wakeup);
2777                         }
2778                 }
2779                 switch_count = &prev->nvcsw;
2780         }
2782         if (prev->on_rq ||rq->skip_clock_update < 0)
2783                 update_rq_clock(rq);/*更新所保持的runqueue的spinlock*/
2785         next = pick_next_task(rq, prev);/*選適合的task到CPU的排程內,放入變數next中*/
2786         clear_tsk_need_resched(prev);/*清除TIF_NEED_RESCHED*/
2787         clear_preempt_need_resched();
2788         rq->skip_clock_update = 0;
/*如果next為內部的thread,他使用prev所使用的地址空間。如果next是一個普通task,用next的地址空間替換prev的地址空間。*/
2790         if (likely(prev != next)) {
/*如果prev和next不是同一個task,將進行context switch*/
2791                 rq->nr_switches++;
2792                 rq->curr = next;
2793                 ++*switch_count;
2795                 context_switch(rq, prev, next); /* unlocks the rq */
2802                 cpu = smp_processor_id();/*更新cpu id*/
2803                 rq = cpu_rq(cpu);
2804         } else/*如果prev和task是同一個task,就不進行context switch
2805                 raw_spin_unlock_irq(&rq->lock);/*釋放spinlock,接受中斷請求*/
2807         post_schedule(rq);
2809         sched_preempt_enable_no_resched();
/*ready queue 會解鎖而插隊的狀態會開啟,如果有process再次發出插隊請求,schedule() function再次被執行*/
2810         if (need_resched())
/*need_resched 是一個flag,會定期的檢查kernel,會告訴kernel哪些process先run,且schedule()應該快速執行*/
2811                 goto need_resched;
2812 }

沒有留言:

張貼留言