linux核心中的個性時鐘nohz與hres(1)

來源:互聯網
上載者:User

設計linux核心的那幫傢伙想的可真周到啊,前面說過,linux核心的性格就是激情,只要硬體設計的足夠靈活,那麼設計者就會儘可能的發揮,不放過任 何可自由發揮的點和死角,而且他們從來不管後果,有時還毅然拋棄硬體的建議,最新核心設計linux核心的那幫傢伙想的可真周到啊,前面說過,linux核心的性格就是激情,只要硬體設計的足夠靈活,那麼設計者就會儘可能的發揮,不放過任 何可自由發揮的點和死角,而且他們從來不管後果,有時還毅然拋棄硬體的建議,最新核心的nohz可謂是一項創舉。時鐘中斷是電腦系統必須的,就像人必須 有心跳一樣,人的心跳是周期的,電腦系統的“心跳”也是周期的,因此,時鐘中斷每隔固定的時間就會發生。

真的是這樣嗎?linux核心的設計者認為如果cpu在空閑態,那麼就沒有必要心跳了,畢竟電腦不是一個自組織系統,能源全靠外界電源供給,而人是一個 自組織實體,因此人必須要有周期的心跳來自己產生能量,電腦的外界電源只要不斷,加上時鐘可程式化,那麼非周期心跳甚至心跳停止就是可能的,linux內 核實現了這一點。在2.6.21核心之前,時鐘中斷是周期的,在那之後引入了新的時鐘封裝結構clock_event_device和 clocksource,於是可以更加靈活的實現自己設計的個性時鐘,這個個性時鐘就是nohz方式和hres方式。當然系統初 啟的時候時鐘中斷還是周期的,當timer_interrupt被調用的時候,就會觸發timer非強制中斷,然後在接下來的非強制中斷處理中找機會切到nohz 或者hres,具體代碼如下:

 
  1. void run_local_timers(void)  
  2. {  
  3. hrtimer_run_queues(); //優先處理高精度時鐘隊列  
  4. raise_softirq(TIMER_SOFTIRQ); //觸發非強制中斷,處理函數見下:  
  5. softlockup_tick();  
  6. }  
  7. static void run_timer_softirq(struct softirq_action *h)
    //非強制中斷處理函數  
  8. {  
  9. struct tvec_base *base = __get_cpu_var(tvec_bases);  
  10. hrtimer_run_pending(); //這裡有機會切換到nohz或者hres  
  11. if (time_after_eq(jiffies, base->timer_jiffies))  
  12. __run_timers(base);  
  13. }  
  14. void hrtimer_run_pending(void)  
  15. {  
  16. struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);  
  17. if (hrtimer_hres_active()) //如果已經是了,就沒有必要切換了,直接返回  
  18. return;  
  19. if (tick_check_oneshot_change(!hrtimer_is_hres_enabled())) 
    //這個if判斷就是具體切換到hres或者nohz的代碼  
  20. hrtimer_switch_to_hres();  
  21. run_hrtimer_pending(cpu_base);  
  22. }  
  23. int tick_check_oneshot_change(int allow_nohz)  
  24. {  
  25. struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);  
  26. if (!test_and_clear_bit(0, &ts->check_clocks))
     //由此開始的種種判斷說明切換所需要到種種條件  
  27. return 0;  
  28. if (ts->nohz_mode != NOHZ_MODE_INACTIVE)  
  29. return 0;  
  30. if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available())  
  31. return 0;  
  32. if (!allow_nohz) //如果hres是允許的,那麼返回1,這樣就會切換到hres高精度模式了  
  33. return 1;  
  34. tick_nohz_switch_to_nohz(); 
    //如果沒有機會切換到高精度模式,前面種種驗證均通過,這裡最起碼切換到了nohz模式  
  35. return 0;  

hres 模式和nohz模式的具體切換由hrtimer_switch_to_hres和tick_nohz_switch_to_nohz負責。不能光一味的跟 蹤代碼,hres和nohz有何關聯呢又分別是什麼意義呢?hres實際上也不是周期中斷的,而是很精確的確定中斷,用最近到時的hrtimer的觸發時 間來對時鐘編程從而在那個時間到來的時候觸發中斷,而nohz僅僅說明可以用非周期的時間對時鐘編程,對精度沒有要求。

在hres中,一切事物都由一個 hrtimer負責,比如原來的節拍調度,統計當前進程的時間等操作直接在timer_interrupt進行,而hres模式下,上述操作專門有一個 hrtimer,當clock_event_device的event_handler執行時所有操作都被封裝進了 clock_event_device的event_handler,而此event_handler在切換到hres或者nohz的時候被賦值),該函 數遍曆所有的hrtimer,所有的hrtimer組織成紅/黑樹狀結構,將到期的hrtimer鏈入一個鏈表,然後在非強制中斷中執行這個鏈表的hrtimer的回 調函數,對於別的hrtimer則馬上執行:所有hrtimer分為兩類,一類不能在非強制中斷中執行,屬於比較緊急的,另一個可以在非強制中斷中執行,屬於不那 麼緊急的。對於純粹的nohz非hres模式,event_handler中還是傳統的處理方式,只不過下次中斷的時間可以任意編程。這種方式中,時間測量可以達到鈉秒的精度。

每當cpu執行cpu_idle的時候,核心就會找機會停掉系統的心跳,然後在適當時機觸發心跳,而不是周期的心跳,這個時機是什麼呢?如果一切都由 hrtimer負責了,那麼這個時機就是找出的最近到期的timer的到期時刻,雖然停掉了周期的時鐘中斷,但是別的硬體中斷是沒有停掉的,而硬體中斷可能觸發一些事件,比如調度,比如發布一個新的timer,因此,每次硬體中斷後都要檢查最新的hrtimer的到期情況和重新調度請求,如果有那麼馬上停 掉關心跳模式切出idle進程。下面的代碼體現了這一點,在每次進入硬體中斷處理的時候都要調用irq_enter:

 
  1. void irq_enter(void)  
  2. {  
  3. #ifdef CONFIG_NO_HZ  
  4. int cpu = smp_processor_id();  
  5. if (idle_cpu(cpu) && !in_interrupt())  
  6. tick_nohz_stop_idle(cpu);  
  7. #endif  
  8. __irq_enter();  
  9. #ifdef CONFIG_NO_HZ  
  10. if (idle_cpu(cpu))  
  11. tick_nohz_update_jiffies(); //更新計時,nohz模式由此來作為觸發下一
    中斷的時機參考。怎麼理解呢?看看這個調用條件,只有在cpu處於idle狀態時
    才更新時間,因為cpu處於idle時可能已經將周期時鐘停掉了,為了不遺失時
    間資訊,必須在中斷中補上。  
  12. #endif  

nohz 模式下的中斷“幾乎”是周期的,nohz的字面意義就是非周期,但是它還是基本周期的,因為它沒有任何下一個時鐘中斷的時間點依據;但是hres卻是完全 隨機時鐘中斷的,因為它的event_handler中就是操作紅/黑樹狀結構上的hrtimer們,因此,它完全可以將下一個到期的hrtimer的到期時刻作為下一個觸發時鐘中斷的時刻,要知道在hres模式裡面,所有的時間相關的操作比如計時,節拍調度等都是由hrtimer負責的,如果要選擇下一次觸發時 鐘中斷的時機就不能在某一個hrtimer的處理函數裡面仲裁了,而必須在全域的處理所有的hrtimer的event_handler函數裡面仲裁,這 就是一切。我們看一下cpu_idle:

 
  1. void cpu_idle(void)  
  2. {  
  3. int cpu = smp_processor_id();  
  4. current_thread_info()->status |= TS_POLLING;  
  5. /* endless idle loop with no priority at all */  
  6. while (1) {  
  7. tick_nohz_stop_sched_tick(1);   
  8. while (!need_resched()) {  
  9. check_pgt_cache();  
  10. rmb();  
  11. if (rcu_pending(cpu))  
  12. rcu_check_callbacks(cpu, 0);  
  13. if (cpu_is_offline(cpu))  
  14. play_dead();  
  15. local_irq_disable();  
  16. __get_cpu_var(irq_stat).idle_timestamp = jiffies;  
  17. /* Don't trace irqs off for idle */  
  18. stop_critical_timings();  
  19. pm_idle();  
  20. start_critical_timings();  
  21. }  
  22. tick_nohz_restart_sched_tick();  
  23. preempt_enable_no_resched();  
  24. schedule();  
  25. preempt_disable();  
  26. }  

其中tick_nohz_stop_sched_tick裡面調用了next_jiffies = get_next_timer_interrupt(last_jiffies);這一句,此句的意思就是找出下一個最近的timer或者hrtimer 用來將其到期時間作為下一個時鐘中斷的時間。在tick_nohz_stop_sched_tick中當然要檢查重新調度標誌,如果置位那麼馬上返回不再 nohz了,其實在每個硬體中斷後的irq_exit裡都要調用tick_nohz_stop_sched_tick函數用來在可能的情況下重新對時鐘編 程。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.