生活随笔
收集整理的這篇文章主要介紹了
基于CFS算法的schedule()源码分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
內核中的調度算法在不斷變化,2.4內核中的調度器是在所有的進程中選擇優先級最高的進程,2.6內核前期的調度器是基于O(1)算法的,而 2.6.23版本之后的內核采用CFS調度算法,并同時對調度器進行了比較大的改善。內核主要是引入了調度器類來增加調度器的可擴展性。調度器類將各種調 度策略模塊化,封裝了對不同調度策略的具體實現。
內核中對進程調度的方法有兩種,其一為周期性調度器(generic scheduler),它對進行進行周期性的調度,以固定的頻率運行;其二為主調度器(main scheduler),如果進程要進行睡眠或因為其他原因主動放棄CPU,那么就直接調用主調度器。
內核的主調度器是通過schedule()實現的,該函數的主要工作就是挑選下一個應該被調度的進程next。
該函數首先禁止內核搶占,并且依次獲取當前CPU編號cpu、當前CPU對應的運行隊列rq、當前進程的切換次數switch_count以及當前進程的描述符prev。
| 1 | asmlinkage void __sched schedule(void) |
| 3 | ????struct task_struct *prev, *next; |
| 4 | ????unsigned long *switch_count; |
| 10 | ????cpu = smp_processor_id(); |
| 14 | ????switch_count = &prev->nivcsw; |
| 16 | ????release_kernel_lock(prev); |
| 17 | need_resched_nonpreemptible: |
| 19 | ????schedule_debug(prev); |
| 21 | ????if (sched_feat(HRTICK)) |
| 22 | ????????hrtick_clear(rq); |
接下來通過update_rq_clock()更新就緒隊列上的時鐘,接著通過clear_tsk_need_resched()清除當前進程prev的重新調度標志TIF_NEED_RESCHED。
| 1 | raw_spin_lock_irq(&rq->lock); |
| 3 | clear_tsk_need_resched(prev); |
如果當前進程是可中斷睡眠狀態(可運性狀態TASK_RUNNING宏的值為0),但它卻收到了某個喚醒它的信號,那么當前進程的標志被更新為TASK_RUNNING,等待再次被調度。否則,通過deactivate_task()將當前進程prev從就緒隊列中刪除。
這里的deactivate_task()根據調度類的不同實現也有所不同,但這些差異對主調度器是透明的,因為調度器類在各種調度實例和調度器之間起到了連接作用。該函數的核心語句即為:
| 1 | p->sched_class->dequeue_task(rq, p, sleep); |
sched_class是進程描述符中描述當前進程所屬調度類的字段,通過這個字段回調鉤子函數dequeue_task()。
| 1 | if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { |
| 2 | ????if (unlikely(signal_pending_state(prev->state, prev))) |
| 3 | ????????prev->state = TASK_RUNNING; |
| 5 | ????????deactivate_task(rq, prev, 1); |
| 6 | ????switch_count = &prev->nvcsw; |
| 11 | if (unlikely(!rq->nr_running)) |
| 12 | ????idle_balance(cpu, rq); |
通過put_prev_task()將prev進程重新插入到就緒隊列合適的位置中。再通過pick_next_task()在當前的就緒隊列中挑選下一個應該被執行的進程next。這兩個函數都屬于調度器類中的鉤子函數,它們的具體實現根據調度實例的不同而不同。
| 1 | put_prev_task(rq, prev); |
| 2 | next = pick_next_task(rq); |
有時候,調度器所選的下一個被執行的進程恰好就是當前進程,那么調度器就不必耗費精力去執行上下文切換,但這種情況不是經常發生的。如果prev和 next不是同一個進程,那么先通過sched_info_switch()更新兩個進程描述符的相關字段,并且更新可運行隊列的相關字段。
接下來調用context_switch()進行prev和next兩個進程的上下文切換,該函數由一段匯編代碼組成。
| 1 | if (likely(prev != next)) { |
| 2 | ????sched_info_switch(prev, next); |
| 3 | ????perf_event_task_sched_out(prev, next); |
| 9 | ????context_switch(rq, prev, next); /* unlocks the rq */ |
| 11 | ?????* the context switch might have flipped the stack from under |
| 12 | ?????* us, hence refresh the local variables. |
| 14 | ????cpu = smp_processor_id(); |
| 17 | ????raw_spin_unlock_irq(&rq->lock); |
切換完畢后,當前的進程就是新選擇的進程,它會開始執行。而被切換出去的進程重新運行時會從切換函數的下一條語句開始執行。
| 3 | ????if (unlikely(reacquire_kernel_lock(current) < 0)) {????? prev = rq->curr; |
| 4 | ????????switch_count = &prev->nivcsw; |
| 5 | ????????goto need_resched_nonpreemptible; |
| 8 | ????preempt_enable_no_resched(); |
| 10 | ????????goto need_resched; |
根據上述對主調度器函數源碼的分析,可以總結出主調度器的主要功能如下:
1.獲取當前進程的描述符以及本地CPU的運行隊列
2.將當前進程prev放入可運行隊列中,等待下一次被重新調度
3.在當前的可運行隊列中選取下一個被調度的新進程next
4.從當前進程切換到新進程
轉載于:https://my.oschina.net/u/174242/blog/72938
總結
以上是生活随笔為你收集整理的基于CFS算法的schedule()源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。