Linux進程管理 (7)實時調度


關鍵詞:RT、preempt_count、RT patch。

 

除了CFS調度器之外,還包括重要的實時調度器,有兩種RR和FIFO調度策略。本章只是一個簡單的介紹。

更詳細的介紹參考《Linux進程管理 (9)實時調度類分析,以及FIFO和RR對比實驗》。

同時為了提高Linux的實時性,Linux社區還維護了realtime相關的補丁。這些補丁的介紹在《Linux實時補丁及其分析》。

 

1. 搶占內核

如果Linux內核不支持搶占,那么進程要么主動要求調度,如schedule()或者cond_resched();要么在系統調用、異常處理和中斷處理完成返回用戶空間前夕

 

在支持可搶占內核中,如果喚醒動作發生在系統調用或者異常處理上下文中,在下一次調用preempt_enable()是會檢查是否需要搶占調度;

中斷處理返回前夕會檢查是否要搶占當前進程,注意這里是中斷返回而不是不支持搶占情況的用戶空間返回

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() 完成調度搶占。

#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 &current_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());
}

 

 

2. 內核實時進展

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

 

 

3. 內核延遲調試工具

內核提供了一些接口、工具,使我們得以一窺調度延遲。常用的有個ftrace的調度器preemptirqoff、等,以及工具latencytop、cyclictest等。

3.1 ftrace preemptirqsoff

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

 

 

3.2 latencytop

latencytop在內核上下文切換時記錄被切換進程的內核棧,然后通過匹配內核棧函數來判斷導致上下文切換的原因。

方便判斷系統出現哪方面的延遲,還能查看某個進程或者線程的延遲情況。

使用latencytop需要安裝libcanberra-gtk-module,並且使能CONFIG_LATENCYTOP(通過Kernel hacking->Latency measuring infrastructure打開)。

執行sudo latencytop,得到如下結果。

整個結果分為三部分,Targets->Cause->Backtrace,分別是進程->問題點->問題點棧回溯。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM