Linux下的內核搶占


2017-03-03


 

很遺憾之前在介紹進程調度的文章中,雖然涉及到了內核搶占,但是卻沒有對其進行深入介紹,今天就稍微總結下內核搶占。

 

  內核搶占在一定程度上減少了對某種事件的響應延遲,這也是內核搶占被引入的目的。之前的內核中,除了顯示調用系統調度器的某些點,內核其他地方是不允許中重新調度的,如果內核在做一些比較復雜的工作,就會造成某些急於處理的事得不到及時響應。針對內核搶占其實本質上也是對當前進程而言(不知道這么描述是否合適),因為內核是為用戶程序提供服務,換言之,其本身不會主動的去執行某個動作。這里內核搶占,重點在於用戶程序請求內核服務時,CPU切換到內核態在執行某個系統服務期間,被搶占。

  當然,即使支持內核搶占,也不是什么時候都可以的,還是要考慮對臨界區的保護。類似於多處理器架構,如果進程A陷入到內核模式訪問某個臨界資源,而在訪問期間,進程B也要訪問臨界區,如果這種搶占被允許,那么就發生了臨界區被重入。所以,在訪問臨界資源時需要禁止內核搶占,在出臨界區則要開啟內核搶占。

  為了支持內核搶占,在進程結構體的thread_info結構中有個preempt_count字段,用以記錄當前內核(活動)是否可以被搶占。當該值為0時,允許被搶占;否則,不允許。

  關於內核搶占有幾個函數:
  

#define preempt_disable() \
do { \
    inc_preempt_count(); \
    barrier(); \
} while (0)


#define preempt_enable() \
do { \
    preempt_enable_no_resched(); \
    barrier(); \
    preempt_check_resched(); \
} while (0)

 

 

#define inc_preempt_count() add_preempt_count(1)
#define dec_preempt_count() sub_preempt_count(1)


# define add_preempt_count(val)    do { preempt_count() += (val); } while (0) # define sub_preempt_count(val) do { preempt_count() -= (val); } while (0)

 

上面兩個函數是禁止和啟用內核搶占。其實就是一個宏定義。禁止內核搶占本質上就是把當前進程的thread_info結構中的preempt_count字段加1,而啟用內核搶占就是減1.注意這里啟用內核搶占之后,調用了preempt_check_resched檢查當前是否需要重新調度,這也是一個宏,實現如下:

#define preempt_check_resched() \
do { \
    if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \
        preempt_schedule(); \
} while (0)

 

 就是在開啟內核搶占之后,檢查下此時是否有比較重要的進程等待執行,如果有,則調用preempt_schedule函數執行調度,可見在顯示開啟內核搶占之后,是觸發內核搶占的一個時機。而調度過程本身是不允許被搶占的。preempt_schedule函數如下

asmlinkage void __sched notrace preempt_schedule(void)
{
    struct thread_info *ti = current_thread_info();

    /*
     * If there is a non-zero preempt_count or interrupts are disabled,
     * we do not want to preempt the current task. Just return..
     */
    if (likely(ti->preempt_count || irqs_disabled()))
        return;

    do {
        add_preempt_count_notrace(PREEMPT_ACTIVE);
        __schedule();
        sub_preempt_count_notrace(PREEMPT_ACTIVE);

        /*
         * Check again in case we missed a preemption opportunity
         * between schedule and now.
         */
        barrier();
    } while (need_resched());
}

 

如果搶占計數器不為0或者當前處於關閉硬件中斷狀態均是不可以被搶占的。

前面提到,在不支持內核搶占的內核中,內核態程序會一直執行,直到返回用戶空間進程時,才會檢查調度。在內核中執行期間,能打斷當前執行的,只有中斷,在硬件中斷來了之后,處理器會根據情況着手處理硬件中斷,在硬件中斷處理完畢需要恢復現場時,若檢查之前的狀態是內核態,則不觸發調度,只有之前狀態是用戶態時才會觸發調度。而在支持內核搶占的內核中,在從硬件中斷返回時,不管是返回用戶態和內核態都會檢查調度,若是返回內核態,檢查當前線程調度標識和搶占標識,若都允許,則可進程調度。這是內核搶占下由增加的一個調度點。

 

 參考資料:

1、linux 3.10.1內核

2、深入linux內核架構

 


免責聲明!

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



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