關鍵詞:RT、preempt_count、RT patch。html
除了CFS調度器以外,還包括重要的實時調度器,有兩種RR和FIFO調度策略。本章只是一個簡單的介紹。linux
更詳細的介紹參考《Linux進程管理 (9)實時調度類分析,以及FIFO和RR對比實驗》。shell
同時爲了提升Linux的實時性,Linux社區還維護了realtime相關的補丁。這些補丁的介紹在《Linux實時補丁及其分析》。安全
若是Linux內核不支持搶佔,那麼進程要麼主動要求調度,如schedule()或者cond_resched();要麼在系統調用、異常處理和中斷處理完成返回用戶空間前夕。框架
在支持可搶佔內核中,若是喚醒動做發生在系統調用或者異常處理上下文中,在下一次調用preempt_enable()是會檢查是否須要搶佔調度;ide
中斷處理返回前夕會檢查是否要搶佔當前進程,注意這裏是中斷返回而不是不支持搶佔狀況的用戶空間返回。函數
struct thread_info成員preempt_count計數表示內核是否能夠被徹底搶佔,當preempt_count爲0時,表示內核能夠被安全搶佔;大於0時則禁止搶佔。工具
preempt_count是32bit,低8位用於搶佔計數PREEMPT_ACTIVE表示一個很大的搶佔計數,一般用於表示搶佔調度。測試
內核提供preempt_disable()來關閉搶佔,preempt_count會加1。preempt_enable()函數打開搶佔,preempt_count減1後判斷是否爲0,並檢查thread_info的TIF_NEED_RESCHED標誌位,若是爲0,則用schedule() 完成調度搶佔。ui
#define preempt_disable() \ do { \ preempt_count_inc(); \----------------------------------------對當前current_thread_info()->preempt_count加1 barrier(); \ } while (0) #define preempt_count_inc() preempt_count_add(1) #define preempt_count_add(val) __preempt_count_add(val) static __always_inline void __preempt_count_add(int val) { *preempt_count_ptr() += val; }
#define preempt_enable() \ do { \ barrier(); \ if (unlikely(preempt_count_dec_and_test())) \-----------------prermpt_count減1後爲0,且TIF_NEED_RESCHED被置位,則進行schedule()調度搶佔。 __preempt_schedule(); \ } while (0) static __always_inline bool __preempt_count_dec_and_test(void) { return !--*preempt_count_ptr() && tif_need_resched();---------對當前preempt_count減1並判斷是否爲0,若是爲0則檢查TIF_NEED_RESCHED } static __always_inline int *preempt_count_ptr(void) { return ¤t_thread_info()->preempt_count; } #define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED)-----測試TIF_NEED_RESCHED是否置位 #define test_thread_flag(flag) \ test_ti_thread_flag(current_thread_info(), flag) #define __preempt_schedule() preempt_schedule() asmlinkage __visible void __sched notrace preempt_schedule(void) { if (likely(!preemptible()))-----------------------------------判斷當前preempt_count是否爲0,而且irq沒有被禁止。 return; preempt_schedule_common();------------------------------------__schedule()調度搶佔。 }
# define preemptible() (preempt_count() == 0 && !irqs_disabled())
static void __sched notrace preempt_schedule_common(void) { do { __preempt_count_add(PREEMPT_ACTIVE); __schedule(); __preempt_count_sub(PREEMPT_ACTIVE); /* * Check again in case we missed a preemption opportunity * between schedule and now. */ barrier(); } while (need_resched()); }
Linux在提升實時性方面取得一系列進展,具體以下:
主要功能 | 內核版本 | 說明 |
Preemption suport | 2.5 | |
PI Mutexes | N/A | PI即Priority Inheritance,優先級繼承的互斥體 |
HR Timer | 2.6.24 | 高精度定時器 |
Preemptive RCU | 2.6.25 | 可搶佔RCU |
IRQ Threads | 2.6.30 | 中斷線程化 |
Forced IRQ Threads | 2.6.39 | 強制中斷線程化 |
Deadline scheduler | 3.14 | Deadline調度器 |
Full Realtime Preemption support | rt-patches | rt.wiki.kernel.org |
內核提供了一些接口、工具,使咱們得以一窺調度延遲。經常使用的有個ftrace的調度器preemptirqoff、等,以及工具latencytop、cyclictest等。
preemptirqsoff能夠跟蹤關閉中斷並禁止進程搶佔代碼的延時,同時記錄關閉的最大時長。
這些tracer能夠在Kernel hacking->Tracers中打開。
查看/sys/kernel/debug/tracing/available_tracers能夠知道當前支持的tracer,裏面有preemptirqsoff、preemptoff、irqsoff三種。
更詳細的解釋參照《Linux ftrace框架介紹及運用》。
下面是一個preemptirqsoff實例,能夠看出禁止搶佔、屏蔽中斷的函數排列。以及最大值的進程信息和發生時的棧信息。
# tracer: preemptirqsoff # # preemptirqsoff latency trace v1.1.5 on 4.4.138-rt155-custom # -------------------------------------------------------------------- # latency: 628 us, #39/39, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:8) # ----------------- # | task: gnome-shell-1775 (uid:1000 nice:0 policy:0 rt_prio:0) # ----------------- # => started at: schedule # => ended at: migrate_disable # # # _--------=> CPU# # / _-------=> irqs-off # | / _------=> need-resched # || / _-----=> need-resched_lazy # ||| / _----=> hardirq/softirq # |||| / _---=> preempt-depth # ||||| / _--=> preempt-lazy-depth # |||||| / _-=> migrate-disable # ||||||| / delay # cmd pid |||||||| time | caller # \ / |||||||| \ | / ... gnome-sh-1775 0....21. 41us!: preempt_count_sub <-_raw_spin_unlock_irq gnome-sh-1775 0....11. 627us : pin_current_cpu <-migrate_disable gnome-sh-1775 0....111 628us : preempt_count_sub <-migrate_disable gnome-sh-1775 0....111 628us : migrate_disable <-migrate_disable gnome-sh-1775 0....111 629us+: trace_preempt_on <-migrate_disable gnome-sh-1775 0....111 686us : <stack trace> => preempt_count_sub => migrate_disable => rt_spin_lock => add_wait_queue => __pollwait => unix_poll => sock_poll => do_sys_poll => SyS_poll => entry_SYSCALL_64_fastpath
latencytop在內核上下文切換時記錄被切換進程的內核棧,而後經過匹配內核棧函數來判斷致使上下文切換的緣由。
方便判斷系統出現哪方面的延遲,還能查看某個進程或者線程的延遲狀況。
使用latencytop須要安裝libcanberra-gtk-module,而且使能CONFIG_LATENCYTOP(經過Kernel hacking->Latency measuring infrastructure打開)。
執行sudo latencytop,獲得以下結果。
整個結果分爲三部分,Targets->Cause->Backtrace,分別是進程->問題點->問題點棧回溯。