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內核架構