**:
早期的linux核心是不可搶占的。它的排程方法是:乙個程序可以通過schedule()函式自願地啟動一次排程。非自願的強制性排程只能發生在每次從系統呼叫返回的前夕以及每次從中斷或異常處理返回到使用者空間的前夕。但是,如果在系統空間發生中斷或異常是不會引起排程的。這種方式使核心實現得以簡化。但常存在下面兩個問題:
當前的linux核心加入了核心搶占(preempt)機制。核心搶占指使用者程式在執行系統呼叫期間可以被搶占,該程序暫時掛起,使新喚醒的高優先順序程序能夠執行。這種搶占並非可以在核心中任意位置都能安全進行,比如在臨界區中的**就不能發生搶占。臨界區是指同一時間內不可以有超過乙個程序在其中執行的指令序列。在linux核心中這些部分需要用自旋鎖保護。
核心搶占要求核心中所有可能為乙個以上程序共享的變數和資料結構就都要通過互斥機制加以保護,或者說都要放在臨界區中。在搶占式核心中,認為如果核心不是在乙個中斷處理程式中,並且不在被 spinlock等互斥機制保護的臨界**中,就認為可以"安全"地進行程序切換。
linux核心將臨界**都加了互斥機制進行保護,同時,還在執行時間過長的**路徑上插入排程檢查點,打斷過長的執行路徑,這樣,任務可快速切換程序狀態,也為核心搶占做好了準備。
linux核心搶占只有在核心正在執行例外處理程式(通常指系統呼叫)並且允許核心搶占時,才能進行搶占核心。禁止核心搶占的情況列出如下:
(1)核心執行中斷處理例程時不允許核心搶占,中斷返回時再執行核心搶占。
(2)當核心執行軟中斷或tasklet時,禁止核心搶占,軟中斷返回時再執行核心搶占。
(3)在臨界區禁止核心搶占,臨界區保護函式通過搶占計數巨集控制搶占,計數大於0,表示禁止核心搶占。
搶占式核心實現的原理是在釋放自旋鎖時或從中斷返回時,如果當前執行程序的 need_resched 被標記,則進行搶占式排程。
struct thread_info;
核心排程器的入口為preempt_schedule(),他將當前程序標記為task_preempted狀態再呼叫schedule(),在task_preempted狀態,schedule()不會將程序從執行佇列中刪除。
核心搶占api函式
在中斷或臨界區**中,執行緒需要關閉核心搶占,因此,互斥機制(如:自旋鎖(spinlock)、rcu等)、中斷**、鍊錶資料遍歷等需要關閉核心搶占,臨界**執行完時,需要開啟核心搶占。關閉/開啟核心搶占需要使用核心搶占api函式preempt_disable和preempt_enable。
核心搶占api函式說明如下(在include/linux/preempt.h中):
preempt_enable() //核心搶占計數preempt_count減1
preempt_disable() //核心搶占計數preempt_count加1
preempt_enable_no_resched() //核心搶占計數preempt_count減1,但不立即搶占式排程
preempt_check_resched () //如果必要進行排程
preempt_count() //返回搶占計數
preempt_schedule() //核搶占時的排程程式的入口點
核心搶占api函式的實現巨集定義列出如下(在include/linux/preempt.h中):
#define preempt_disable() \
dowhile(0
)
#define inc_preempt_count() \
do while (0)
#define preempt_count() (current_thread_info()->preempt_count)
linux核心在硬中斷或軟中斷返回時會檢查執行搶占排程。分別說明如下:
(1)硬中斷返回執行搶占排程
linux核心在硬中斷或出錯退出時執行函式retint_kernel,執行搶占函式,函式retint_kernel列出如下(在arch/x86/entry_64.s中):
#ifdef config_preempt/* 返回到核心空間,檢查是否需要執行搶占*/
/* 暫存器rcx存放threadinfo位址,此時,中斷關閉*/
entry(retint_kernel)
cmpl $0
,threadinfo_preempt_count(
%rcx)
jnz retint_restore_args
bt $tif_need_resched,threadinfo_flags(
%rcx)
jnc retint_restore_args
bt $9
,eflags-argoffset(
%rsp)
/* 中斷是否關閉? */
jnc retint_restore_args
call preempt_schedule_irq
jmp exit_intr
#endif
函式preempt_schedule_irq是出中斷上下文時核心搶占排程的入口點,該函式被呼叫和返回時中斷應關閉,保護此函式從中斷遞迴呼叫。該函式列出如下(在kernel/sched.c中):
asmlinkage void __sched preempt_schedule_irq(void
)while
(unlikely(test_thread_flag(tif_need_resched)))
;}
排程函式schedule會檢測程序的 preempt_counter 是否很大,避免普通排程時又執行核心搶占排程。
(2)軟中斷返回執行搶占排程
在開啟頁出錯函式pagefault_enable和軟中斷底半部開啟函式local_bh_enable中,會呼叫函式preempt_check_resched檢查是否需要執行核心搶占。如果不是並能排程,程序才可執行核心搶占排程。函式preempt_check_resched列出如下:
#define preempt_check_resched() \do while(0
)
函式preempt_schedule源**與函式preempt_schedule_irq基本上一樣,對程序進行排程,這裡不再分析。
Linux 核心搶占
核心搶占 kernel preemption 是一種有效的降低系統響應延時的方法。在核心裡,有三個相關的配置選項。顧名思義,不開啟核心搶占。這是很多伺服器的預設配置選項。在沒有開啟核心搶占的時候,程序的排程時機僅僅發生在非常有限的幾處 程序自己主動觸發排程,例如 通過sleep schedule y...
Linux核心 了解Linux核心搶占
目錄 無強制搶占 可搶占核心 自願核心搶占 完全實時搶占 在配置linux核心時,我們可以設定一些影響系統行為的引數。您可以使用不同的優先順序,排程類和搶占模型。了解並選擇正確的引數非常重要。在這篇文章中,我將介紹不同的搶占模型,以及每種模型如何影響使用者和核心行為 如果配置核心 使用make me...
linux核心搶占及配置
1 linux 核心 linux 2.6以前 是不可搶占的,但支援使用者搶占它的排程方法是 乙個程序可以通過schedule 函式自願地啟動一次排程。非自願的強制性排程只能發生在每次從系統呼叫返回的前夕以及每次從中斷或異常處理返回到使用者空間的前夕 這種強制性排程又叫使用者搶占 但是,如果在系統空間...