Linux實時性- PREEMPT_RT 實時搶占實現(xiàn)

?
PREEMPT_RT的原理
?
PREEMPT_RT包的關(guān)鍵點是要使非搶占式的內(nèi)核代碼量盡可能的少,同時為了提供搶占性而必須修改的代碼量也要盡可能的少。特別是臨界區(qū),中斷處理程序和中斷禁用的代碼序列通常是可搶占式的。PREEMPT_RT包充分利用Linux內(nèi)核的SMP能力來增加額外的搶占能力,而不是重寫Linux內(nèi)核。某種程度上,可以大致認(rèn)為搶占是給系統(tǒng)新加了一個CPU,然后使用常規(guī)鎖定原語與搶占任務(wù)采取的操作進(jìn)行同步。
?
注意:這里說的一些原理不要從字面意思上去理解。比如PREEMPT_RT包執(zhí)行每一個搶占并不是執(zhí)行一個CPU熱拔插事件。相反,關(guān)鍵點是提供幾乎容忍無限制搶占的底層機(jī)制是必須提供SMP環(huán)境。后面的章節(jié)會詳細(xì)介紹如何應(yīng)用這些原理以及如何使用。
?
PREEMPT_RT的功能
?
PREEMPT_RT包有如下特性:
搶占式臨界區(qū)
搶占式中斷處理
搶占式中斷禁止代碼序列
內(nèi)核自旋鎖和信號量的優(yōu)先級繼承
遞延操作
降低延遲的措施
搶占式臨界區(qū)
?
在PREEMPT_RT中,普通的自旋鎖(spinlock_t and rwlock_t)是搶占式的,RCU讀取側(cè)臨界區(qū)((rcu_read_lock() 和rcu_read_unlock())也是一樣的。信號量臨界區(qū)是可搶占的,他們已經(jīng)存在于可搶占和非搶占內(nèi)核中。這種可搶占性意思是可以阻止獲取自旋鎖,也就是在可搶占或中斷禁用的情況下獲取自旋鎖是非法的(這個原則的一個例外就是變體_trylock,只要不是在密集信號中重復(fù)調(diào)用)。這也意味著當(dāng)使用spinlock_t的時候spin_lock_irqsave()不會禁用硬件中斷。
?
問題1:如何在非搶占內(nèi)核中實現(xiàn)搶占式信號量臨界區(qū)?
在中斷或搶占禁用的情況下要獲取鎖要做什么?用raw_spinlock_t而不是spinlock_t,調(diào)用spin_lock()的時候使用raw_spinlock_t。PREEMPT_RT包含一個宏集合,這樣會讓spin_lock()調(diào)用的時候就像c++中的函數(shù)重載。當(dāng)使用raw_spinlock_t的時候,就是傳統(tǒng)的自旋鎖。但是當(dāng)使用spinlock_t,臨界區(qū)就是可搶占的。當(dāng)使用raw_spinlock_t時,各種_irq原語(例如spin_lock_irqsave())會禁用硬件中斷,而在使用spinlock_t時不會禁用硬件中斷。但是,使用raw_spinlock_t(及其對應(yīng)的rwlock_t,raw_rwlock_t)應(yīng)該是例外,而不是常規(guī)使用。在一些底層區(qū)域比如調(diào)度,特定的架構(gòu)代碼和RCU,是不需要這些原始鎖的。
?
由于臨界區(qū)可以被搶占,就不能依賴單個CPU上給定的臨界區(qū),因為可能會移到其他CPU上。所以,當(dāng)你在臨界區(qū)使用per-CPU變量時,必須單獨(dú)處理搶占的可能性。因為spinlock_t和rwlock_t不再具有這個功能。
?
可以通過以下兩種方式實現(xiàn):
顯示禁用中斷,或者通過調(diào)用get_cpu_var(), preempt_disable(),或者禁掉硬件中斷。
使用per-CPU鎖來保護(hù)per-CPU變量,可以通過使用新的DEFINE_PER_CPU_LOCKED()原語。
由于spin_lock可以睡眠,所以會增加一個額外的任務(wù)狀態(tài)。思考一下下面的代碼序列:
spin_lock(&mylock1);
current->state=TASK_UNINTERRUPTIBLE;
spin_lock(&mylock2); // [*]
blah();
spin_unlock(&mylock2);
spin_unlock(&mylock1);
?
由于第二個spin_lock()調(diào)用可以睡眠,所以有可能會改變current-state的值,有可能使函數(shù)blah()產(chǎn)生令人驚訝的結(jié)果。在這種情況下,調(diào)度程序可以使用新的TASK_RUNNING_MUTEX位來保留current-state之前的值。盡管生成的環(huán)境有點陌生,但是通過少量的代碼改動就實現(xiàn)了臨界區(qū)搶占,并且PREEMPT_RT, PREEMPT和 non-PREEMPT三個配置項都是用相同的代碼。
?
搶占式中斷處理
?
在PREEMPT_RT環(huán)境中幾乎所有的進(jìn)程上下文都有中斷處理。雖然任何標(biāo)為SA_NODELAY的中斷都可以在其上下文中運(yùn)行,但是僅在fpu_irq, irq0, irq2和lpptest指定了SA_NODELAY。其中,只有irq0(per-CPU計時器中斷)可以正常使用。fpu-irq是用于浮點協(xié)處理器中斷,而lpptest是用于中斷等待時間基準(zhǔn)測試。注意軟件計時器(add_timer())不在硬件上下文中運(yùn)行。它是運(yùn)行在進(jìn)程上下文中,并且是完全搶占式的。
?
注意不要輕易使用SA_NODELAY,因為它會大大降低中斷和調(diào)度延遲。Per-CPU計時器中斷之所以符合條件,是因為它與調(diào)度程序和其他核心內(nèi)核組件緊密相關(guān)。此外,在寫SA_NODELAY中斷處理代碼的時候必須要非常謹(jǐn)慎,否則很容易出現(xiàn)崩潰和死鎖。
?
由于per-CPU計時器中斷運(yùn)行在硬件中斷上下文中,因此任何和進(jìn)程上下文代碼共享的鎖必須是原始自旋鎖(raw_spinlock_t 或 raw_rwlock_t)。并且,從進(jìn)程上下文獲取時,必須使用_irq變體,比如spin_lock_irqsave()。另外,當(dāng)進(jìn)程上下文代碼訪問每個和SA_NODELAY中斷處理程序共享的per-CPU變量的時候,一般上要禁用硬件中斷。
?
搶占式“中斷禁用”代碼序列
?
搶占式中斷禁用代碼序列的概念從術(shù)語上理解似乎是矛盾的,但是牢記PREEMPT_RT原理很重要。原理就是要依靠Linux內(nèi)核的SMP功能來處理和中斷處理程序的競爭。大多數(shù)中斷處理程序都運(yùn)行在進(jìn)程上下文中。任何與中斷處理程序有交互的代碼都要準(zhǔn)備處理在其他CPU上同時運(yùn)行的該中斷處理程序。
?
因此,spin_lock_irqsave()和相關(guān)的原語不需要禁用搶占。之所以安全的原因是,即使中斷處理程序運(yùn)行,即使它搶占了擁有spinlock_t的代碼,但是在試圖獲取spinlock_t的時候會立即阻塞。臨界區(qū)依舊會被保留。
?
但是,local_irq_save()依舊禁用搶占,因為沒有任何鎖依賴它。因此使用鎖而不是local_irq_save()可以降低調(diào)度延遲,但是以這種方式替換鎖會降低SMP性能,因此要小心。
?
需要和SA_NODELAY中斷交互的代碼不能使用local_irq_save(),因為它沒用禁用硬件中斷。相反,應(yīng)該使用raw_local_irq_save(),類似的,當(dāng)需要和SA_NODELAY中斷處理程序交互的時候,需要使用原始自旋鎖(raw_spinlock_t, raw_rwlock_t 和raw_seqlock_t)。但是原始自旋鎖和原始中斷禁用不應(yīng)該在一些底層區(qū)域,如調(diào)度程序,架構(gòu)依賴代碼和RCU之外使用。
?
內(nèi)核自旋鎖和信號量的優(yōu)先級繼承
?
實時程序員會經(jīng)常擔(dān)心優(yōu)先級倒置,這可能會發(fā)生一下幾種情況:
低優(yōu)先級任務(wù)A獲取資源,比如獲取鎖
中優(yōu)先級任務(wù)B開始執(zhí)行CPU綁定,搶占低優(yōu)先級任務(wù)A
高優(yōu)先級任務(wù)C試圖獲取低優(yōu)先任務(wù)A持有的鎖,但是被阻塞了。因為中優(yōu)先級任務(wù)B已經(jīng)搶占了低優(yōu)先級任務(wù)A
這種優(yōu)先級倒置可以無限期地延遲高優(yōu)先級任務(wù)。有兩種方式可以解決這個問題:(1)抑制搶占;(2)優(yōu)先級繼承。第一種情況,由于沒有搶占,所以任務(wù)B不能搶占任務(wù)A,從而阻止優(yōu)先級反轉(zhuǎn)的發(fā)生。這種方式在PREEMPT內(nèi)核中用于自旋鎖,但不用于信號量。抑制搶占對于信號量來說是沒有意義的。因為持有一個信號量的時候阻塞是合法的,即使沒有搶占也會導(dǎo)致優(yōu)先級反轉(zhuǎn)。對于某些實時工作負(fù)載,自旋鎖也不能抑制搶占,因為會對調(diào)度延遲造成影響。
?
優(yōu)先級繼承可以用在搶占抑制沒有意義的場合。就是高優(yōu)先級任務(wù)臨時把優(yōu)先級贈與持有關(guān)鍵鎖的低優(yōu)先級任務(wù)。優(yōu)先級繼承是可以傳遞的:在上面的例子中,如果更高優(yōu)先級任務(wù)D試圖獲取高優(yōu)先級任務(wù)C已經(jīng)持有的第二把鎖,任務(wù)C和A都將暫時提升為任務(wù)D的優(yōu)先級。優(yōu)先級提升的持續(xù)時間也受到嚴(yán)重限制:一旦低優(yōu)先級任務(wù)A釋放了鎖,它會立刻失去臨時提升的優(yōu)先級,把鎖交給任務(wù)C。
?
但是,任務(wù)C運(yùn)行需要時間,很可能同時另一個更高優(yōu)先級任務(wù)E來試圖獲取鎖。如果發(fā)生這種情況,任務(wù)E會從任務(wù)C那里偷到鎖。這樣是合法的,因為任務(wù)C還沒有運(yùn)行,因此實際上它并沒有獲取鎖。另一方面,如果任務(wù)C在任務(wù)E試圖獲取鎖之前已經(jīng)運(yùn)行,那么任務(wù)E是無法偷鎖的,必須等待任務(wù)C釋放鎖,可能會提高任務(wù)C的優(yōu)先級以加快處理速度。
?
另外,在某些情況下會長時間保持鎖定。其中一些增加了“搶占點”,以便鎖持有者在某些其他任務(wù)需要時丟棄該鎖。
?
事實證明,讀寫優(yōu)先級繼承特別成問題。因此,盡管任務(wù)可以遞歸獲取,但Preempt_RT可以通過一次只允許一個任務(wù)獲取讀寫鎖或信號量來簡化這個為題。盡管限制了可擴(kuò)展性,但這讓優(yōu)先級繼承實現(xiàn)成為可能。
?
問題2:實現(xiàn)讀寫優(yōu)先級繼承的簡單快捷的方法是什么?
此外,在某些情況下,信號量不需要優(yōu)先級繼承,比如當(dāng)信號量用于事件機(jī)制而不是鎖的時候。compat_semaphore 和compat_rw_semaphore變體可以用于這種情況。很多信號量原語(up(), down()等)可用于compat_semaphore 和compat_rw_semaphore。相同的,讀寫信號量原語(up_read(), down_write()等)可用于compat_rw_semaphore 和rw_semaphore。
?
總結(jié)一下,優(yōu)先級繼承可以防止優(yōu)先級反轉(zhuǎn),允許高優(yōu)先級任務(wù)及時獲取鎖和信號量,即使這些鎖和信號量被低優(yōu)先級任務(wù)持有。PREEMPT_RT的優(yōu)先級繼承具有傳遞性且能夠及時移除,并且具有當(dāng)高優(yōu)先級任務(wù)突然需要低優(yōu)先任務(wù)持有的鎖時,處理這種情況的靈活性。當(dāng)信號量用于事件機(jī)制的時候,compat_semaphore 和compat_rw_semaphore可以避免優(yōu)先級繼承。
?
遞延操作
?
由于spin_lock()現(xiàn)在可以休眠,所以當(dāng)搶占或中斷被禁用的時候,調(diào)用它就不再合法了。在一些情況下,可以通過遞延操作要求spin_lock()等到搶占被重新啟用的時候來解決這個問題。
當(dāng)合法獲取task_struct中的spinlock_t alloc_lock是,可以將put_task_struct()放到put_task_struct_delayed()隊列中,以便延遲運(yùn)行。
把mmdrop()放到mmdrop_delayed()隊列中,延遲運(yùn)行。
TIF_NEED_RESCHED_DELAYED重新調(diào)度,不過需要等到進(jìn)程返回到用戶空間,或者等到下一個preempt_check_resched_delayed()。無論哪種方式,關(guān)鍵點在于避免在喚醒高優(yōu)先級任務(wù)直到當(dāng)前任務(wù)未鎖定之前無法取得進(jìn)展的情況下進(jìn)行不必要的搶占。沒有TIF_NEED_RESCHED_DELAYED,高優(yōu)先級任務(wù)會立刻搶占低優(yōu)先級任務(wù),只能被快速阻塞等待低優(yōu)先級任務(wù)持有的鎖。
解決方案是在spin_unlock()之后增加wake_up()去替代wake_up_process_sync()。如果喚醒的進(jìn)程搶占當(dāng)前進(jìn)程,通過TIF_NEED_RESCHED_DELAYED,喚醒操作會被延遲。
?
在所有這些情況下,解決方案是將操作推遲到可以更安全或更方便地執(zhí)行該操作。
?
降低延遲的操作
?
在PREEMPT_RT中的一些改變,主要目的是降低調(diào)度或中斷延遲。
?
第一種改變是引入x86 MMX/SSE硬件。這個硬件在內(nèi)核中處理中斷禁用。某些情況下意味著等待直到MMX/SSE指令完成。一些MMX/SSE指令沒有問題,但是有些指令要花很長時間,所以PREEMPT_RT拒絕使用這些很慢的指令。
?
第二個改變是使用per-CPU變量用于板坯分配器,以代替之前隨意的中斷禁用。
?
PREEMPT_RT原語總結(jié)
?
這個章節(jié)總結(jié)PREEMPT_RT增加的原語列表或者原來的行為幾乎被PREEMPT_RT改變的原語列表。
?
鎖原語
spinlock_t
關(guān)鍵臨界區(qū)是搶占式的。_irq操作沒有禁用硬件中斷。優(yōu)先級繼承用來防止優(yōu)先級反轉(zhuǎn)。rt_mutex在PREEMPT_RT用來實現(xiàn)spinlock_t(包括rwlock_t, struct semaphore和struct rw_semaphore)
raw_spinlock_t
提供spinlock_t原有功能的的特定變種,所以臨界區(qū)是非搶占的,_irq真的禁用了硬件中斷。需要注意的是在raw_spinlock_t上應(yīng)該使用正常原語(比如spin_lock())。也就是,除了特定架構(gòu)或者底層調(diào)度與同步原語外禁止使用raw_spinlock_t。誤用raw_spinlock_t會破壞PREEMPT_RT的實時性。
rwlock_t
關(guān)鍵臨界區(qū)是搶占式的。_irq操作沒有禁用硬件中斷。優(yōu)先級繼承用來防止優(yōu)先級反轉(zhuǎn)。為了保持優(yōu)先級繼承窒息的復(fù)雜度,每個任務(wù)只允許讀取/獲取一次給定的rwlock_t,即使這個任務(wù)會遞歸讀取/獲取rwlock_t。
RW_LOCK_UNLOCKED(mylock)
RW_LOCK_UNLOCKED宏根據(jù)優(yōu)先級繼承的要求蔣鎖自身作為參數(shù)。但是,這樣使用的話,與搶占和非搶占的內(nèi)核都是不兼容的。使用RW_LOCK_UNLOCKED因此要改為DEFINE_RWLOCK()。
raw_rwlock_t
提供rwlock_t原有功能的特定變種,所以臨界區(qū)是非搶占的,_irq真的禁用了硬件中斷。需要注意的是在raw_rwlock_t上應(yīng)該使用正常原語(比如read_lock ())。也就是,除了特定架構(gòu)或者底層調(diào)度與同步原語外禁止使用raw_rwlock_t。誤用raw_rwlock_t會破壞PREEMPT_RT的實時性。
seqlock_t
臨界區(qū)是搶占式的。更新操作已經(jīng)使用優(yōu)先級繼承。(讀取操作不需要優(yōu)先級繼承因為seqlock_t讀者不能阻塞寫操作)
SEQLOCK_UNLOCKED(name)
SEQLOCK_UNLOCKED宏根據(jù)優(yōu)先級繼承的要求將鎖自身作為參數(shù)。但是,這樣使用與搶占和非搶占的內(nèi)核都是不兼容的。使用SEQLOCK_UNLOCKED因此要改為DECLARE_SEQLOCK ()。注意DECLARE_SEQLOCK()定義并初始化seqlock_t。
struct semaphore
semaphore受優(yōu)先級繼承的約束。
down_trylock()
這個原理用于調(diào)度,因此不能在禁用硬件中斷和禁用搶占的情況下使用。但是幾乎所有的中斷都需要在啟用了搶占和中斷的進(jìn)程上下文中允許,所以這個限制目前沒有任何影響。
struct compat_semaphore
結(jié)構(gòu)體semaphore的變種,不受優(yōu)先級繼承的約束。這個結(jié)構(gòu)體在事件機(jī)制下非常有用,對睡眠鎖沒用。
struct rw_semaphore
結(jié)構(gòu)體rw_semaphore受繼承優(yōu)先級約束,并且一個任務(wù)每次只能讀取一次。但是,這個任務(wù)可以遞歸的讀取rw_semaphore.
struct compat_rw_semaphore
結(jié)構(gòu)體rw_semaphore的變種,不受優(yōu)先級繼承的約束。這個結(jié)構(gòu)體在事件機(jī)制下非常有用,對睡眠鎖沒用。
?
Per-CPU?變量
DEFINE_PER_CPU_LOCKED(type, name)
DECLARE_PER_CPU_LOCKED(type, name)
定義/聲明有指定類型和名字的per-CPU變量,但是也要定義/聲明相應(yīng)的spinlock_t。如果有一組per-CPU變量需要回旋鎖的保護(hù),要把它們分組到一個結(jié)構(gòu)體中。
get_per_cpu_locked(var, cpu)
返回指定CPU的指定的per-CPU變量,但是只能在獲取相應(yīng)的自旋鎖之后。
put_per_cpu_locked(var, cpu)
釋放指定CPU相應(yīng)的自旋鎖給指定的per-CPU變量。
per_cpu_lock(var, cpu)
釋放指定CPU相應(yīng)的自旋鎖給指定的per-CPU變量,但是是作為左值。當(dāng)調(diào)用的函數(shù)的參數(shù)是一個將要釋放的自旋鎖試非常有用。
per_cpu_locked(var, cpu)
將指定 CPU 的指定 per-CPU 變量作為左值返回,但不獲取鎖,大概是因為已經(jīng)獲取了鎖但需要獲取對該變量的另一個引用?;蛘呖赡苁且驗檎趯ψ兞窟M(jìn)行 RCU 讀取端引用,因此不需要獲取鎖。
?
中斷處理
SA_NODELAY
在結(jié)構(gòu)體irqaction使用,指定直接調(diào)用在硬件中斷上下文中相應(yīng)的中斷處理程序,而不是移交線程irq。函數(shù)redirect_hardirq()負(fù)責(zé)喚醒,在do_irqd()函數(shù)中可以找到中斷處理循環(huán)。
注意:SA_NODELAY不能用于正常的設(shè)備中斷。
會降低中斷和調(diào)度延遲
SA_NODELAY中斷處理程序的編碼和維護(hù)比普通的中斷處理程序要困難。只在低級別的中斷或需要極端實時延遲的硬件中斷使用SA_NODELAY
local_irq_enable()
local_irq_disable()
local_irq_save(flags)
local_irq_restore(flags)
irqs_disabled()
irqs_disabled_flags()
local_save_flags(flags)
local_irq*() 函數(shù)實際上并沒有禁用硬件中斷,它們只是禁用了搶占。這些適用于普通中斷,但不適用于 SA_NODELAY 中斷處理程序。
然而,對于 PREEMPT_RT 環(huán)境,使用鎖(可能是per-CPU 的鎖)而不是這些函數(shù)通常會更好——但也 要考慮對使用非 搶占內(nèi)核的 SMP 機(jī)器的影響!
raw_local_irq_enable()
raw_local_irq_disable()
raw_local_irq_save(flags)
raw_local_irq_restore(flags)
raw_irqs_disabled()
raw_irqs_disabled_flags()
raw_local_save_flags(flags)
這些函數(shù)禁用了硬件中斷,因此適用于SA_NODELAY中斷。這些函數(shù)特定只在低級代碼中使用,例如調(diào)度程序、同步原語等。注意,在 raw_local_irq*() 的影響下,無法獲得正常的 spinlock_t 鎖。
?
其他項
wait_for_timer()
等待指定的計時器到期。這是必需的,因為定時器在 PREEMPT_RT 環(huán)境中運(yùn)行,因此可以被搶占,也可以阻塞,比如如在 spinlock_t 獲取期間。
smp_send_reschedule_allbutself()
將重新調(diào)度 IPI 發(fā)送到所有其他 CPU。這在調(diào)度器中用于快速找到另一個 CPU 來運(yùn)行新喚醒的高優(yōu)先級實時任務(wù),但沒有足夠高的優(yōu)先級在當(dāng)前 CPU 上運(yùn)行。這種能力對于進(jìn)行實時所需的高效全局調(diào)度是必要的。非實時任務(wù)繼續(xù)以傳統(tǒng)方式按 CPU 進(jìn)行調(diào)度,犧牲一些優(yōu)先級的準(zhǔn)確性以提高效率和可擴(kuò)展性。
INIT_FS(name)
將變量的名稱作為參數(shù),以便內(nèi)部 rwlock_t 可以正確初始化(考慮到優(yōu)先級繼承的需要)
local_irq_disable_nort()
local_irq_enable_nort()
local_irq_save_nort(flags)
local_irq_restore_nort(flags)
spin_lock_nort(lock)
spin_unlock_nort(lock)
spin_lock_bh_nort(lock)
spin_unlock_bh_nort(lock)
BUG_ON_NONRT()
WARN_ON_NONRT()
這些在 PREEMPT_RT 中什么都不做(或幾乎什么都不做),但在其他環(huán)境中具有正常效果。這些原語不應(yīng)在低級代碼之外使用(例如,在調(diào)度程序、同步原語或特定于體系結(jié)構(gòu)的代碼中)。
spin_lock_rt(lock)
spin_unlock_rt(lock)
in_atomic_rt()
BUG_ON_RT()
WARN_ON_RT()
這些在 PREEMPT_RT 中有正常的效果,但在其他環(huán)境中什么也不做。同樣,這些原語不應(yīng)在低級代碼之外使用(例如,在調(diào)度程序、同步原語或特定于體系結(jié)構(gòu)的代碼中)。
smp_processor_id_rt(cpu)
在 PREEMPT_RT 環(huán)境中返回“cpu”,但在其他環(huán)境中的作用與 smp_processor_id() 相同。這僅用于slab分配器。
?
PREEMPT_RT配置選項
?
High-Level Preemption-Option Selection
PREEMPT_NONE:為服務(wù)器操作系統(tǒng)選擇傳統(tǒng)的非搶占內(nèi)核
PREEMPT_VOLUNTARY:啟動自愿搶占點,但是不能批發(fā)內(nèi)核搶占。這個主要是桌面操作系統(tǒng)使用
PREEMPT_DESKTOP:啟用自愿搶占點以及非關(guān)鍵部分搶占 。適用于低延遲桌面操作系統(tǒng)使用。
PREEMPT_RT:啟用完全搶占,包括臨界區(qū)。
Feature-Selection Configuration Options
PREEMPT:啟用非臨界區(qū)內(nèi)核搶占
PREEMPT_BKL :大內(nèi)核鎖臨界區(qū)搶占.
PREEMPT_HARDIRQS:硬中斷在進(jìn)程上下文中允許,從而可搶斷。但是標(biāo)記為SA_NODELAY的irqs繼續(xù)在硬件中斷上下文中進(jìn)行。
PREEMPT_RCU :RCU讀側(cè)臨界區(qū)可搶占。
PREEMPT_SOFTIRQS :軟中斷在進(jìn)程上下文中進(jìn)行,從而可搶占。
調(diào)試配置項
?
有些可能已經(jīng)發(fā)生了變化,但是可以了解下PREEMPT_RT可以提供的調(diào)試種類:
CRITICAL_PREEMPT_TIMING:?測量內(nèi)核在禁用搶占的情況下花費(fèi)的最長時間
CRITICAL_IRQSOFF_TIMING :測量內(nèi)核在禁用硬件中斷請求的情況下花費(fèi)的最長事件。
DEBUG_IRQ_FLAGS:內(nèi)核驗證spin_unlock_irqrestore()和其他類似原語的“flg“參數(shù)。
DEBUG_RT_LOCKING_MODE:啟用從可搶占到不可搶占的自旋鎖的運(yùn)行事件切換。對于想要評估 PREEMPT_RT 機(jī)制開銷的內(nèi)核開發(fā)人員很有用。
DETECT_SOFTLOCKUP:內(nèi)核在轉(zhuǎn)儲任何進(jìn)程當(dāng)前堆棧跟蹤,超過10秒不需要內(nèi)核重新調(diào)度。
LATENCY_TRACE :記錄表示長延遲事件的函數(shù)調(diào)用跟蹤。這些跟蹤可以通過 /proc/latency_trace 從內(nèi)核中讀出??梢酝ㄟ^/proc/sys/kernel/preempt_thresh 過濾掉低延遲跟蹤。這個選項在跟蹤過度低延時非常有用。
LPPTEST:執(zhí)行基于并行端口的延遲測量的設(shè)備驅(qū)動程序,使用 scripts/testlpp.c 實際運(yùn)行此測試
PRINTK_IGNORE_LOGLEVEL :-all-printk() 消息被轉(zhuǎn)儲到控制臺。通常不是什么好方法,但在其他調(diào)試工具失敗時很有幫助。
RT_DEADLOCK_DETECT:發(fā)現(xiàn)死鎖循環(huán)。
RTC_HISTOGRAM :使用 /dev/rtc 為應(yīng)用程序生成延遲直方圖數(shù)據(jù)。
WAKEUP_TIMING :測量從高優(yōu)先級線程被喚醒到它實際開始運(yùn)行的最長時間(以微秒為單位)。結(jié)果是從 /proc/sys/kernel/wakeup_timing 訪問的。并且可以通過 echo 0 > /proc/sys/kernel/preempt_max_latency 重新啟動測試
?
快速問答
?
問題#1: 如何在非搶占內(nèi)核中實現(xiàn)搶占信號量臨界區(qū)?
嚴(yán)格來說,搶占根本不會發(fā)生在不可搶占的內(nèi)核中。但是,由于訪問用戶數(shù)據(jù)時的頁面錯誤以及顯式調(diào)用調(diào)度程序等原因,可能會發(fā)生大致相同的事情。
?
問題#2: 實現(xiàn)從寫入者到多個讀寫者的優(yōu)先級繼承的簡單快捷的方法是什么?
沒有已知的解決方案,并且已經(jīng)進(jìn)行了相當(dāng)徹底的討論。特別是,在考慮提高寫入者到讀寫者的優(yōu)先級時,請考慮這樣一種情況,即一個讀寫鎖被多個讀者讀取持有,并且每個讀者都被阻止嘗試寫入/獲取其他一些讀寫鎖。然后每次讀寫鎖再次被多個讀者讀取持有。當(dāng)然,提升(然后取消提升)所有這些讀者所需的時間會影響調(diào)度延遲。
?
問題#3: 為什么事件機(jī)制不能使用優(yōu)先級繼承?
Linux?無法確定要提升哪個任務(wù)
使用睡眠鎖,獲取信號量的任務(wù)可能是釋放信號量的任務(wù),因此可以確定是會提升優(yōu)先級的任務(wù)。相比之下,對于事件,任何任務(wù)都可能執(zhí)行喚醒高優(yōu)先級任務(wù)的 down()。
PREEMPT_RT意想不到的影響
由于 PREEMPT_RT 環(huán)境嚴(yán)重依賴以 SMP 安全方式編碼的 Linux,因此使用 PREEMPT_RT 清除了 Linux 內(nèi)核中的許多 SMP 錯誤,包括一些定時器死鎖、ns83820_tx_timeout() 中的鎖遺漏,ACPI-空閑調(diào)度延遲錯誤、核心網(wǎng)絡(luò)鎖定錯誤以及塊 IO 統(tǒng)計代碼中的許多搶占式錯誤。
?
參考鏈接:
https://lwn.net/Articles/146861/
https://tldp.org/HOWTO/Parallel-Processing-HOWTO-2.html
https://www.halolinux.us/kernel-architecture/the-big-kernel-lock.html
https://lwn.net/Articles/262464/
原文作者:Paul E. McKenney
本文譯者:王紅燕 -?Elektrobit中國團(tuán)隊的專家
相關(guān)產(chǎn)品:
Elektrobit提供車規(guī)級高性能操作系統(tǒng)產(chǎn)品——EB corbos Linux。EB corbos Linux是面向汽車工業(yè)的基于容器的Linux分發(fā)版。容器概念解決了“不同應(yīng)用程序之間的依賴性管理”這一主要難題,為客戶擴(kuò)展提供了可變性,并能夠?qū)崿F(xiàn)有效的維護(hù)。為了滿足較高的安全性和可靠性要求,EB corbos Linux可配備一個硬化Linux內(nèi)核和根文件系統(tǒng),用來確保所有階段的系統(tǒng)完整性。此外,EB corbos Linux還可為各種CPU供應(yīng)商集成板級支持包和特定補(bǔ)丁。訪問官網(wǎng)獲得更多產(chǎn)品技術(shù)詳情:https://www.elektrobit.cn/products/ecu/eb-corbos/linux/