日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux实时进程优先级rt,Linux实时性- PREEMPT_RT实时抢占实现

發布時間:2023/12/4 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux实时进程优先级rt,Linux实时性- PREEMPT_RT实时抢占实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:Paul E. McKenney

翻譯整理:土豆絲624

原文鏈接:

概述:

本篇文章主要講Linux的實時包PREEMPT_RT 是如何實現的。

PREEMPT_RT 的原理

PREEMPT_RT包的關鍵點是要使非搶占式的內核代碼量盡可能的少,同時為了提供搶占性而必須修改的代碼量也要盡可能的少。特別是臨界區,中斷處理程序和中斷禁用的代碼序列通常是可搶占式的。PREEMPT_RT包充分利用Linux內核的SMP能力來增加額外的搶占能力,而不是重寫Linux內核。某種程度上,可以認為是搶占是給系統新加了一個CPU,然后使用常規鎖定原語與搶占任務采取的任何操作進行同步。

注意:這里說的一些原理不要從字面意思上去理解。比如PREEMPT_RT包對每個搶占并不是一個熱拔插事件。關鍵點是使用搶占必須提供SMP機制。后面的章節會詳細介紹如何應用這些原理。

PREEMPT_RT的功能

PREEMPT_RT包有如下特性

搶占式臨界區

搶占式中斷處理

搶占式中斷禁止代碼序列

內核自旋鎖和信號量的優先級繼承

遞延操作

降低延遲的措施

搶占式臨界區

在PREEMPT_RT中,普通的自旋鎖(spinlock_t and rwlock_t)是搶占式的,RCU讀取側臨界區((rcu_read_lock() 和rcu_read_unlock())也是一樣的。信號量臨界區是可搶占的,他們已經存在于可搶占和非搶占內核中。這種可搶占性意思是可以阻止獲取自旋鎖,也就是在可搶占或中斷禁用的情況下獲取自旋鎖是非法的(這個原則的一個例外就是變體_trylock,只要不是在密集信號中重復調用)。這也意味著當使用spinlock_t的時候spin_lock_irqsave()不會禁用硬件中斷。

問題1:如何在非搶占內核中實現搶占式信號量臨界區?

在中斷或搶占禁用的情況下要獲取鎖要做什么?用raw_spinlock_t而不是spinlock_t,調用spin_lock()的時候使用raw_spinlock_t。PREEMPT_RT包含一個宏集合,這樣會讓spin_lock()調用的時候就像c++中的函數重載。當使用raw_spinlock_t的時候,就是傳統的自旋鎖。但是當使用spinlock_t,臨界區就是可搶占的。當使用raw_spinlock_t時,各種_irq原語(例如spin_lock_irqsave())會禁用硬件中斷,而在使用spinlock_t時不會禁用硬件中斷。但是,使用raw_spinlock_t(及其對應的rwlock_t,raw_rwlock_t)應該是例外,而不是常規使用。在一些底層區域比如調度,特定的架構代碼和RCU,是不需要這些原始鎖的。

自從臨界區可以被搶占,就不能依賴單個CPU上給定的臨界區。因為是可搶占的,所以可能會移到其他的CPU上。所以,當你在臨界區使用per-CPU變量時,必須單獨處理搶占的可能性。因為spinlock_t和rwlock_t不再具有這個功能。

可以通過以下兩種方式實現。

1. 顯示禁用中斷,或者通過調用get_cpu_var(), preempt_disable(),或者禁掉硬件中斷

2. 使用per-CPU鎖來保護per-CPU變量,可以通過使用新的DEFINE_PER_CPU_LOCKED()原語。

由于spin_lock可以睡眠,所以會增加一個額外的任務狀態。思考一下下面的代碼序列

spin_lock(&mylock1);

current->state = TASK_UNINTERRUPTIBLE;

spin_lock(&mylock2); // [*]

blah();

spin_unlock(&mylock2);

spin_unlock(&mylock1);

由于第二個spin_lock()調用可以睡眠,所以有可能會改變current-state的值,有可能使函數blah()產生令人驚訝的結果。在這種情況下,調度程序可以使用新的TASK_RUNNING_MUTEX位來保留current-state之前的值。盡管生成的環境有點陌生,但是通過少量的代碼改動就實現了臨界區搶占,并且PREEMPT_RT, PREEMPT和 non-PREEMPT三個配置項都是用相同的代碼。

搶占式中斷處理

在Preempt_RT環境中幾乎所有的進程上下文都有中斷處理。雖然任何標為SA_NODELAY的中斷都可以在其上下文中運行,但是僅在fpu_irq, irq0, irq2和lpptest指定了SA_NODELAY。其中,只有irq0(per-CPU計時器中斷)可以正常使用。fpu-irq是用于浮點協處理器中斷,而lpptest是用于中斷等待時間基準測試。注意軟件計時器(add_timer())不在硬件上下文中運行。它是運行在進程上下文中,并且是完全搶占式的。

注意不要輕易使用SA_NODELAY,因為它會大大降低中斷和調度延遲。Per-CPU計時器中斷之所以符合條件,是因為它與調度程序和其他核心內核組件緊密相關。此外,在寫SA_NODELAY中斷處理代碼的時候必須要非常謹慎,否則很容易出現崩潰和死鎖。

由于per-CPU計時器中斷運行在硬件中斷上下文中,因此任何和進程上下文代碼共享的鎖必須是原始自旋鎖(raw_spinlock_t 或 raw_rwlock_t)。并且,從進程上下文獲取時,必須使用_irq變體,比如spin_lock_irqsave()。另外,當進程上下文代碼訪問每個和SA_NODELAY中斷處理程序共享的per-CPU變量的時候,一般上要禁用硬件中斷。

搶占式“中斷禁用”代碼序列

搶占式中斷禁用代碼序列的概念從術語上理解似乎是矛盾的,但是牢記PREEMPT_RT原理很重要。原理就是要依靠Linux內核的SMP功能來處理和中斷處理程序的競爭。大多數中斷處理程序都運行在進程上下文中。任何與中斷處理程序有交互的代碼都要準備處理在其他CPU上同時運行的該中斷處理程序。

因此,spin_lock_irqsave()和相關的原語不需要禁用搶占。之所以安全的原因是,即使中斷處理程序運行,即使它搶占了擁有spinlock_t的代碼,但是在試圖獲取spinlock_t的時候會立即阻塞。臨界區依舊會被保留。

但是,local_irq_save()依舊禁用搶占,因為沒有任何鎖依賴它。因此使用鎖而不是local_irq_save()可以降低調度延遲,但是以這種方式替換鎖會降低SMP性能,因此要小心。

需要和SA_NODELAY中斷交互的代碼不能使用local_irq_save(),因為它沒用禁用硬件中斷。相反,應該使用raw_local_irq_save(),類似的,當需要和SA_NODELAY中斷處理程序交互的時候,需要使用原始自旋鎖(raw_spinlock_t, raw_rwlock_t 和raw_seqlock_t)。但是原始自旋鎖和原始中斷禁用不應該在一些底層區域,如調度程序,架構依賴代碼和RCU之外使用。

內核自旋鎖和信號量的優先級繼承

實時程序員會經常擔心優先級倒置,這可能會發生一下幾種情況:

低優先級任務A獲取資源,比如獲取鎖

中優先級任務B開始執行CPU綁定,搶占低優先級任務A

高優先級任務C試圖獲取低優先任務A持有的鎖,但是被阻塞了。因為中優先級任務B已經搶占了低優先級任務A

這種優先級倒置可以無限期地延遲高優先級任務。有兩種方式可以解決這個問題:(1)抑制搶占;(2)優先級繼承。第一種情況,由于沒有搶占,所以任務B不能搶占任務A,從而阻止優先級反轉的發生。這種方式在PREEMPT內核中用于自旋鎖,但不用于信號量。抑制搶占對于信號量來說是沒有意義的。因為持有一個信號量的時候阻塞是合法的,即使沒有搶占也會導致優先級反轉。對于某些實時工作負載,自旋鎖也不能抑制搶占,因為會對調度延遲造成影響。

優先級繼承可以用在搶占抑制沒有意義的場合。就是高優先級任務臨時把優先級贈與持有關鍵鎖的低優先級任務。優先級繼承是可以傳遞的:在上面的例子中,如果更高優先級任務D試圖獲取高優先級任務C已經持有的第二把鎖,任務C和A都將暫時提升為任務D的優先級。優先級提升的持續時間也受到嚴重限制:一旦低優先級任務A釋放了鎖,它會立刻失去臨時提升的優先級,把鎖交給任務C。

但是,任務C運行需要時間,很可能同時另一個更高優先級任務E來試圖獲取鎖。如果發生這種情況,任務E會從任務C那里偷到鎖。這樣是合法的,因為任務C還沒有運行,因此實際上它并沒有獲取鎖。另一方面,如果任務C在任務E試圖獲取鎖之前已經運行,那么任務E是無法偷鎖的,必須等待任務C釋放鎖,可能會提高任務C的優先級以加快處理速度。

另外,在某些情況下會長時間保持鎖定。其中一些增加了“搶占點”,以便鎖持有者在某些其他任務需要時丟棄該鎖。

事實證明,讀寫優先級繼承特別成問題。因此,盡管任務可以遞歸獲取,但Preempt_RT可以通過一次只允許一個任務獲取讀寫鎖或信號量來簡化這個為題。盡管限制了可擴展性,但這讓優先級繼承實現成為可能。

問題2:實現讀寫優先級繼承的簡單快捷的方法是什么

此外,在某些情況下,信號量不需要優先級繼承,比如當信號量用于事件機制而不是鎖的時候。compat_semaphore 和compat_rw_semaphore變體可以用于這種情況。很多信號量原語(up(), down()等)可用于compat_semaphore 和compat_rw_semaphore。相同的,讀寫信號量原語(up_read(), down_write()等)可用于compat_rw_semaphore 和rw_semaphore。

總結一下,優先級繼承可以防止優先級反轉,允許高優先級任務及時獲取鎖和信號量,即使這些鎖和信號量被低優先級任務持有。PREEMPT_RT的優先級繼承具有傳遞性且能夠及時移除,并且具有當高優先級任務突然需要低優先任務持有的鎖時,處理這種情況的靈活性。當信號量用于事件機制的時候,compat_semaphore 和compat_rw_semaphore可以避免優先級繼承。

遞延操作

由于spin_lock()現在可以休眠,所以當搶占或中斷被禁用的時候,調用它就不再合法了。在一些情況下,可以通過遞延操作要求spin_lock()等到搶占被重新啟用的時候來解決這個問題。

當合法獲取task_struct中的spinlock_t alloc_lock是,可以將put_task_struct()放到put_task_struct_delayed()隊列中,以便延遲運行。

把mmdrop()放到mmdrop_delayed()隊列中,延遲運行。

TIF_NEED_RESCHED_DELAYED重新調度,不過需要等到進程返回到用戶空間,或者等到下一個preempt_check_resched_delayed()。無論哪種方式,關鍵點在于避免在喚醒高優先級任務直到當前任務未鎖定之前無法取得進展的情況下進行不必要的搶占。沒有TIF_NEED_RESCHED_DELAYED,高優先級任務會立刻搶占低優先級任務,只能被快速阻塞等待低優先級任務持有的鎖。

解決方案是在spin_unlock()之后增加wake_up()去替代wake_up_process_sync()。如果喚醒的進程搶占當前進程,通過TIF_NEED_RESCHED_DELAYED,喚醒操作會被延遲。

在所有這些情況下,解決方案是將操作推遲到可以更安全或更方便地執行該操作。

降低延遲的操作

在PREEMPT_RT中的一些改變,主要目的是降低調度或中斷延遲。

第一種改變是引入x86 MMX/SSE硬件。這個硬件在內核中處理中斷禁用。某些情況下意味著等待直到MMX/SSE指令完成。一些MMX/SSE指令沒有問題,但是有些指令要花很長時間,所以PREEMPT_RT拒絕使用這些很慢的指令。

第二個改變是使用per-CPU變量用于板坯分配器,以代替之前隨意的中斷禁用。

總結

以上是生活随笔為你收集整理的linux实时进程优先级rt,Linux实时性- PREEMPT_RT实时抢占实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。