Linux核心搶占機制 preempt

2021-09-28 10:36:12 字數 3240 閱讀 8006

早期的linux核心(2.5.4版本之前)是不可搶占的。它的排程方法是:乙個程序可以通過schedule()函式自願地啟動一次排程。非自願的強制性排程只能發生在 每次從系統呼叫返回的前夕以及每次從中斷或異常處理返回到使用者空間的前夕。但是,如果在系統空間發生中斷或異常是不會引起排程的。這種方式使核心實現得以 簡化。但常存在下面兩個問題: 

(一)如果這樣的中斷發生在核心中,本次中斷返回是不會引起排程的,而要到最初使cpu從使用者空間進入核心空間的那次系統呼叫或中斷(異常)返回時才會發生排程。 

(二)另外乙個問題是優先順序反轉。在linux中,在核心態執行的任何操作都要優先於使用者態程序,這就有可能導致優先順序反轉問題的出現。例如,乙個低優先順序的使用者程序由於執行軟/硬中斷等原因而導致乙個高優先順序的任務得不到及時響應。

linux核心(2.6版本)加入了核心搶占(preempt)機制。核心搶占指使用者程式在執行系統呼叫期間可以被搶占,該程序暫時掛起,使新喚醒的高優先 級程序能夠執行。這種搶占並非可以在核心中任意位置都能安全進行,比如在臨界區中的**就不能發生搶占。臨界區是指同一時間內不可以有超過乙個程序在其中 執行的指令序列。在linux核心中這些部分需要用自旋鎖保護。 

核心搶占要求核心中所有可能為乙個以上程序共享的變數和資料結構就都要通過互斥機制加以保護,或者說都要放在臨界區中。在搶占式核心中,認為如果內 核不是在乙個中斷處理程式中,並且不在被 spinlock等互斥機制保護的臨界**中,就認為可以」安全」地進行程序切換。 

linux核心將臨界**都加了互斥機制進行保護,同時,還在執行時間過長的**路徑上插入排程檢查點,打斷過長的執行路徑,這樣,任務可快速切換程序狀態,也為核心搶占做好了準備。

linux核心搶占只有在核心正在執行例外處理程式(通常指系統呼叫)並且允許核心搶占時,才能進行搶占核心。禁止核心搶占的情況列出如下: 

(1)核心執行中斷處理例程時不允許核心搶占,中斷返回時再執行核心搶占。 

(2)當核心執行軟中斷或tasklet時,禁止核心搶占,軟中斷返回時再執行核心搶占。 

(3)在臨界區禁止核心搶占,臨界區保護函式通過搶占計數巨集控制搶占,計數大於0,表示禁止核心搶占。

struct thread_info ; 

#endif

核心排程器的入口為preempt_schedule(),他將當前程序標記為task_preempted狀態再呼叫schedule(),在task_preempted狀態,schedule()不會將程序從執行佇列中刪除。

在中斷或臨界區**中,執行緒需要關閉核心搶占,因此,互斥機制(如:自旋鎖(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() / do while (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 

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是出中斷上下文時核心搶占排程的入口點,該函式被呼叫和返回時中斷應關閉,保護此函式從中斷遞迴呼叫。排程函式schedule會檢測程序的 preempt_counter 是否很大,避免普通排程時又執行核心搶占排程。該函式列出如下(在kernel/sched.c中):

asmlinkage void __sched preempt_schedule_irq(void)  while (unlikely(test_thread_flag(tif_need_resched))); }
(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核心搶占實現機制分析

2.6新的可搶占式核心是指核心搶占,即當程序位於核心空間時,有乙個更高優先順序的任務出現時,如果當前核心允許搶占,則可以將當前任務掛起,執行優先順序更高的程序。在2.5.4 版本之前,linux 核心是不可搶占的,高優先順序的程序不能中止正在核心中執行的低優先順序的程序而搶占 cpu執行。程序一旦處...

Linux 核心搶占

核心搶占 kernel preemption 是一種有效的降低系統響應延時的方法。在核心裡,有三個相關的配置選項。顧名思義,不開啟核心搶占。這是很多伺服器的預設配置選項。在沒有開啟核心搶占的時候,程序的排程時機僅僅發生在非常有限的幾處 程序自己主動觸發排程,例如 通過sleep schedule y...

Linux核心 了解Linux核心搶占

目錄 無強制搶占 可搶占核心 自願核心搶占 完全實時搶占 在配置linux核心時,我們可以設定一些影響系統行為的引數。您可以使用不同的優先順序,排程類和搶占模型。了解並選擇正確的引數非常重要。在這篇文章中,我將介紹不同的搶占模型,以及每種模型如何影響使用者和核心行為 如果配置核心 使用make me...