從Linux角度看使用者態與核心態不同的搶占策略

2021-06-22 14:41:05 字數 4679 閱讀 7240

1.非搶占式和可搶占式核心的區別

為了簡化問題,我使用嵌入式實時系統uc/os作為例子。首先要指出的是,uc/os只有核心態,沒有使用者態,這和linux不一樣。

多工系統中,核心負責管理各個任務,或者說為每個任務分配cpu時間,並且負責任務之間的通訊。核心提供的基本服務是任務切換。排程(scheduler),英文還有一詞叫dispatcher,也是排程的意思。這是核心的主要職責之一,就是要決定該輪到哪個任務執行了。多數實時核心是基於優先順序排程法的。每個任務根據其重要程度的不同被賦予一定的優先順序。基於優先順序的排程法指,cpu總是讓處在就緒態的優先順序最高的任務先執行。然而,究竟何時讓高優先順序任務掌握cpu的使用權,有兩種不同的情況,這要看用的是什麼型別的核心,是不可剝奪型的還是可剝奪型核心。

非搶占式核心

非搶占式核心是由任務主動放棄cpu的使用權。非搶占式排程法也稱作合作型多工,各個任務彼此合作共享乙個cpu。非同步事件還是由中斷服務來處理。中斷服務可以使乙個高優先順序的任務由掛起狀態變為就緒狀態。但中斷服務以後控制權還是回到原來被中斷了的那個任務,直到該任務主動放棄cpu的使用權時,那個高優先順序的任務才能獲得cpu的使用權。非搶占式核心如下圖所示。

非搶占式核心的優點有:

·中斷響應快(與搶占式核心比較);

·允許使用不可重入函式;

·幾乎不需要使用訊號量保護共享資料。執行的任務占有cpu,不必擔心被別的任務搶占。這不是絕對的,在印表機的使用上,仍需要滿足互斥條件。

非搶占式核心的缺點有:

·任務響應時間慢。高優先順序的任務已經進入就緒態,但還不能執行,要等到當前執行著的任務釋放cpu。

·非搶占式核心的任務級響應時間是不確定的,不知道什麼時候最高優先順序的任務才能拿到cpu的控制權,完全取決於應用程式什麼時候釋放cpu。

搶占式核心

使用搶占式核心可以保證系統響應時間。最高優先順序的任務一旦就緒,總能得到cpu的使用權。當乙個執行著的任務使乙個比它優先順序高的任務進入了就緒態,當前任務的cpu使用權就會被剝奪,或者說被掛起了,那個高優先順序的任務立刻得到了cpu的控制權。如果是中斷服務子程式使乙個高優先順序的任務進入就緒態,中斷完成時,中斷了的任務被掛起,優先順序高的那個任務開始執行。搶占式核心如下圖所示。

搶占式核心的優點有:

·使用搶占式核心,最高優先順序的任務什麼時候可以執行,可以得到cpu的使用權是可知的。使用搶占式核心使得任務級響應時間得以最優化。

搶占式核心的缺點有:

·不能直接使用不可重入型函式。呼叫不可重入函式時,要滿足互斥條件,這點可以使用互斥型訊號量來實現。如果呼叫不可重入型函式時,低優先順序的任務cpu的使用權被高優先順序任務剝奪,不可重入型函式中的資料有可能被破壞。

2.linux下的使用者態搶占和核心態搶占

linux除了核心態外還有使用者態。使用者程式的上下文屬於使用者態,系統呼叫和中斷處理例程上下文屬於核心態。在2.6 kernel以前,linux kernel只支援使用者態搶占。

2.1 使用者態搶占(user preemption)

在kernel返回使用者態(user-space)時,並且need_resched標誌為1時,scheduler被呼叫,這就是使用者態搶占。當kernel返回使用者態時,系統可以安全的執行當前的任務,或者切換到另外乙個任務。當中斷處理例程或者系統呼叫完成後,kernel返回使用者態時,need_resched標誌的值會被檢查,假如它為1,排程器會選擇乙個新的任務並執行。中斷和系統呼叫的返回路徑(return path)的實現在entry.s中(entry.s不僅包括kernel entry code,也包括kernel exit code)。

2.2 核心態搶占(kernel preemption)

在2.6 kernel以前,kernel code(中斷和系統呼叫屬於kernel code)會一直執行,直到code被完成或者被阻塞(系統呼叫可以被阻塞)。在 2.6 kernel裡,linux kernel變成可搶占式。當從中斷處理例程返回到核心態(kernel-space)時,kernel會檢查是否可以搶占和是否需要重新排程。kernel可以在任何時間點上搶占乙個任務(因為中斷可以發生在任何時間點上),只要在這個時間點上kernel的狀態是安全的、可重新排程的。

3.核心態搶占的設計

3.1 可搶占的條件

要滿足什麼條件,kernel才可以搶占乙個任務的核心態呢?

·沒持有鎖。鎖是用於保護臨界區的,不能被搶占。

·kernel code可重入(reentrant)。因為kernel是smp-safe的,所以滿足可重入性。

如何判斷當前上下文(中斷處理例程、系統呼叫、核心執行緒等)是沒持有鎖的?linux在每個每個任務的thread_info結構中增加了preempt_count變數作為preemption的計數器。這個變數初始為0,當加鎖時計數器增一,當解鎖時計數器減一。

3.2 核心態需要搶占的觸發條件

核心提供了乙個need_resched標誌(這個標誌在任務結構thread_info中)來表明是否需要重新執行排程。

3.3 何時觸發重新排程

set_tsk_need_resched():設定指定程序中的need_resched標誌

clear_tsk need_resched():清除指定程序中的need_resched標誌

need_resched():檢查need_ resched標誌的值;如果被設定就返回真,否則返回假

什麼時候需要重新排程:

·時鐘中斷處理例程檢查當前任務的時間片,當任務的時間片消耗完時,scheduler_tick()函式就會設定need_resched標誌;

·訊號量、等到佇列、completion等機制喚醒時都是基於waitqueue的,而waitqueue的喚醒函式為default_wake_function,其呼叫try_to_wake_up將被喚醒的任務更改為就緒狀態並設定need_resched標誌。

·設定使用者程序的nice值時,可能會使高優先順序的任務進入就緒狀態;

·改變任務的優先順序時,可能會使高優先順序的任務進入就緒狀態;

·新建乙個任務時,可能會使高優先順序的任務進入就緒狀態;

·對cpu(smp)進行負載均衡時,當前任務可能需要放到另外乙個cpu上執行;

3.4 搶占發生的時機(何時檢查可搶占條件)

·當乙個中斷處理例程退出,在返回到核心態時(kernel-space)。這是隱式的呼叫schedule()函式,當前任務沒有主動放棄cpu使用權,而是被剝奪了cpu使用權。

·當kernel code從不可搶占狀態變為可搶占狀態時(preemptible again)。也就是preempt_count從正整數變為0時。這也是隱式的呼叫schedule()函式。

·乙個任務在核心態中顯式的呼叫schedule()函式。任務主動放棄cpu使用權。

·乙個任務在核心態中被阻塞,導致需要呼叫schedule()函式。任務主動放棄cpu使用權。

3.5 禁用/使能可搶占條件的操作

對preempt_count操作的函式有add_preempt_count()、sub_preempt_count()、inc_preempt_count()、dec_preempt_count()。

使能可搶占條件的操作是preempt_enable(),它呼叫dec_preempt_count()函式,然後再呼叫preempt_check_resched()函式去檢查是否需要重新排程。

禁用可搶占條件的操作是preempt_disable(),它呼叫inc_preempt_count()函式。

在核心中有很多函式呼叫了preempt_enable()和preempt_disable()。比如spin_lock()函式呼叫了preempt_disable()函式,spin_unlock()函式呼叫了preempt_enable()函式。

3.6 什麼時候不允許搶占

preempt_count()函式用於獲取preempt_count的值,preemptible()用於判斷核心是否可搶占。

有幾種情況linux核心不應該被搶占,除此之外,linux核心在任意一點都可被搶占。這幾種情況是:

·核心正進行中斷處理。在linux核心中程序不能搶占中斷(中斷只能被其他中斷中止、搶占,程序不能中止、搶占中斷),在中斷例程中不允許進行程序排程。程序排程函式schedule()會對此作出判斷,如果是在中斷中呼叫,會列印出錯資訊。

·核心正在進行中斷上下文的bottom half(中斷的下半部)處理。硬體中斷返回前會執行軟中斷,此時仍然處於中斷上下文中。

·核心的**段正持有spinlock自旋鎖、writelock/readlock讀寫鎖等鎖,處乾這些鎖的保護狀態中。核心中的這些鎖是為了在smp系統中短時間內保證不同cpu上執行的程序併發執行的正確性。當持有這些鎖時,核心不應該被搶占,否則由於搶占將導致其他cpu長期不能獲得鎖而死等。

·核心正在執行排程程式scheduler。搶占的原因就是為了進行新的排程,沒有理由將排程程式搶占掉再執行排程程式。

·核心正在對每個cpu「私有」的資料結構操作(per-cpu date structures)。在smp中,對於per-cpu資料結構未用spinlocks保護,因為這些資料結構隱含地被保護了(不同的cpu有不一樣的per-cpu資料,其他cpu上執行的程序不會用到另乙個cpu的per-cpu資料)。但是如果允許搶占,但乙個程序被搶占後重新排程,有可能排程到其他的cpu上去,這時定義的per-cpu變數就會有問題,這時應禁搶占。

事實上,處於核心臨界區中的程式是不能被排程的,但是處於使用者臨界區中的程式卻是可以的。臨界區分為使用者臨界區和核心臨界區

Linux 核心態與使用者態

使用者態 ring3 執行於使用者態的 則要受到處理器的諸多檢查,它們只能訪問對映其位址空間的頁表項中規定的在使用者態下可訪問頁面的虛擬位址,且只能對任務狀態段 tss 中i o 許可位圖 i o permission bitmap 中規定的可訪問埠進行直接訪問 核心態 ring0 在處理器的儲存保...

Linux核心態與使用者態

如上圖所示,從巨集觀上來看,linux作業系統的體系架構分為使用者態和核心態 或者使用者空間和核心 核心從本質上看是一種軟體 控制計算機的硬體資源,並提供上層應用程式執行的環境。使用者態即上層應用程式的活動空間,應用程式的執行必須依託於核心提供的資源,包括cpu資源 儲存資源 i o資源等。為了使上...

linux核心態使用者態

概述 linux系統使用者態和核心態相關知識,linux核心版本為3.10.79。使用者態和核心態定義 核心態 cpu可以訪問記憶體所有資源,包括外圍裝置.可以進行處理器工作模式切換,程式的切換。使用者態 只能受限的訪問記憶體,且不允許訪問外圍裝置,不可以進行處理器模式切換。為什麼分使用者態和核心態...