RT进程组的cpu带宽限制
1.背景
由于實時任務的優先級高于普通任務,因而為了防止cpu消耗型的實時任務一直占用cpu引發其他任務"饑餓"的情況發生,內核采用了帶寬限制手段來抑制實時任務的運行時間。系統中將各個任務按層級組織成一個個任務組,組內的所有任務視為一個整體掛在一個運行隊列上,而帶寬限制的單位也是針對一個組來進行的。
在任務調度中帶寬限制就是指一定周期內一個隊列上任務可運行的最大時間,內核中使用xxx_bandwidth結構來限制任務的運行時間。針對實時任務這個結構就是:
(1)實時任務CPU使用率的檢測周期
(2)Rt_period周期內實時任務允許的額定運行時間
(3)執行周期監控的高精度定時器
rt_bandwidth限制了進程組就緒隊列rt_rq上的實時任務在rt_period周期內運行時間不能夠超過rt_runtime;而在SMP多cpu環境中rt_bandwidth限制了系統中實時任務在rt_period周期內的cpu占用時間比例不能夠超過rt_runtime/rt_period。一個典型的SMP系統中我們通常限制實時進程在1s內的最大運行時間為0.95s。
實時進程組的帶寬限制需要解決兩個問題:首先是何時開始限制進程組,第二是何時解除進程組的限制。下面圍繞這兩個方面進行分析。
2. 實時任務調度限制的實現
實時調度器會在更新進程時間統計信息的時候去檢查實時進程是否運行過長時間,這是樸素的設計思想,所以首先從update_curr_rt開始分析。
2.1 實時任務的時間信息更新
(1)如果不是實時任務,那么直接返回。
(2)計算當前任務從上一次更新運行時間到現在的運行時間delta_exec。
(3)統計最大運行時間exec_max。
(4)統計當前實時任務總的運行時間sum_exec_runtime。
(5)從新賦值實時任務的起始運行時間exec_start。
(6)從當前實時任務所在的group循環到root group,更新每一個group任務隊列的運行時間。前面有講到帶寬限制是針對taskgroup,所以每一個任務的更新都會導致自己所屬組的運行時間rt_time的增加。
(7)根據當前任務找到所屬組的任務隊列:
static inline struct rt_rq *rt_rq_of_se(struct sched_rt_entity *rt_se)
{
return rt_se->rt_rq;
}
(8)把當前任務本次更新時的運行時間加入所屬組的運行時間。
(9)調用sched_rt_runtime_exceeded檢查當前運行的實時任務的所屬組的任務隊列是否超時。
(10)如果超時(比如大于950ms)那么調用resched_curr設置當前運行的實時任務的TIF_NEED_RESCHED標志,這樣下一個調度時刻就可以搶占當前運行的實時任務。
2.2 實時任務的超時檢查
(1)獲取當前group實時運行隊列的最大實時運行時間:
static inline u64 sched_rt_runtime(struct rt_rq *rt_rq)
{
if (!rt_rq->tg)
return RUNTIME_INF;
return rt_rq->rt_runtime;
}
(2)如果當前組的實時就緒隊列處于throttled狀態,那么直接返回該狀態。
(3)如果設置的最大runtime不小于檢測周期,那么直接返回0,表示不用throttle。
(4)某些場景下我們需要對實時就緒隊列的最大runtime做平衡調整。(在多核情況下,其他cpu上的rt_rq還有剩余時間,可以從其他cpu的rt_rq中"借"時間)
(5)取出平衡調整后的最大runtime。
(6)如果實時就緒隊列rt_rq累積的運行時間rt_time超過了最大允許的運行時間rt_runtime,那么就需要對當前的實時就緒隊列做throttle。
(7)設置當前實時就緒隊列的rt_throttled標志為1。
(8)在有些系統中不允許實時進程運行時間過長,那么這個時候可以在這里觸發panic,具體可以在dump_throttled_rt_tasks里面調用BUG()。
(9)最后調用rt_rq_throttled再次檢查throttled標志,如果沒有問題就調用sched_rt_rq_dequeue把當前實時就緒隊列對應的調度實體從所屬的實時就緒隊列上刪除:
3. 實時任務調度限制的解除
前文講到實時任務帶寬限制針對的是實時任務所在的就緒隊列,每一個實時任務的運行都會對所屬的實時就緒隊列的實時運行時間rt_time有所貢獻,在實時任務時間數據更新的時候我們都會去檢查rt_time有沒有超過rt_runtime,如果超過就會執行throttle同時把當前實時任務所在的實時就緒隊列對應的調度實體從就緒隊列上刪除。
從上面的邏輯中我們可以看到實時就緒隊列的實時運行時間rt_time是一個累加值,我們判斷是否應該執行帶寬限制的一句是rt_time是否超過了rt_runtime,超過就要進行限制,否則不限制。因此rt_time不可能一直增加,一旦調度限制已經讓任務得到了應有的“懲罰”,就需要解除這個限制, 內核中由struct task_group的rt_bandwidth的高精度時鐘rt_period_timer來實現此功能。
3.1 rt_period_timer的初始化
首先task_group里面有對實時進程帶寬限制的描述:
然后在進程組創建的時候會對rt_bandwidth進行初始化:
sched_create_group–>alloc_rt_sched_group–>init_rt_bandwidth
這里rt_period被賦值為1000000000ns,rt_runtime被賦值為950000000ns。緊接著初始化rt_period_timer,hrtimer的回調為sched_rt_period_timer
3.2 rt_period_timer觸發邏輯
(1).enqueue_task流程
也就是說當有實時進程添加進就緒隊列的時候觸發hrtimer進行超時監控:
enqueue_task_rt
–>enqueue_rt_entity
–>__enqueue_rt_entity
–>inc_rt_tasks
–>inc_rt_group
–>start_rt_bandwidth
–>hrtimer_forward_now(&rt_b->rt_period_timer, ns_to_ktime(0));
hrtimer_start_expires(&rt_b->rt_period_timer, HRTIMER_MODE_ABS_PINNED);
可以看到這是一個立馬觸發hrtimer回調的流程。
(2)sched_rt_rq_enqueue流程
也就是說當我們把實時就緒隊列放入其所屬的就緒隊列的時候需要觸發hrtimer進行超時監控,當rt_rq受限解除的時候需要這個操作來重新開始超時監控。另外這里有個調度器的.rq_offline操作我們暫不分析:
.rq_offline
–>rq_offline_rt
–>__disable_runtime
–>sched_rt_rq_enqueue
–>enqueue_rt_entity
3.3 rt_period_timer回調處理
(1)設置hrtimer的超時時間為rt_period(1s),返回時鐘超時的期數overrun。
(2)主要的時鐘處理函數,下面詳細分析。
(3)如果do_sched_rt_period_timer返回的idle等于1,表示此task_group中沒有可調度的任務,這個時候時鐘標志rt_period_active設置為未激活。
(1)因為每一顆cpu上面都有task_group對應的rt_rq需要處理,所以這里需要for循環。
(2)根據rt_b找到cpu i上對應task_group對應的rt_rq。
(3)如果rt_rq->rt_time大于0并且當前rt_rq為受限狀態,那么調用balance_runtime以嘗試從其他cpu的rt_rq偷時間。
(4)獲取balance后的限制時間runtime。
(5)在rt_time中抹去周期運行時間overrun*runtime,沒有到一個周期則將運行時間rt_time清零。如果rt_time超過一個周期那么rt_time設置為超出一個周期多出來的時間。
(6)如果更新后的運行時間rt_time小于額定時間并且當前處于受限狀態,那么清除調度受限標志,并將入隊標志設置為1。
(7)如果rt_nr_running非零,證明當前rt_rq上有任務需要調度,設置idle=0。
(8)如果此周期rt_rq沒有運行時間,但是rt_rq上還有就緒任務且rt_rq沒有調度受限,那么設置入隊標志queue=1.
(9)如果入隊標志為1,這個時候我們調用sched_rt_rq_enqueue把當前的rt_rq入隊,這個就是周期性清除task_group調度受限的關鍵操作。
(10)最后返回idle表示此task_group中是否還有就緒任務需要調度。返回1表示沒有就緒任務需要調度了。
小結:從上面do_sched_rt_period_timer(rt_b, overrun)函數也可以看到隊列的帶寬限制的解除條件:在時鐘到期后重新計算rt_rq的運行時間(也就是剩余的運行時間),如果更新后的運行時間小于一個周期的額定時間,則會解除rt_rq的調度限制rt_rq->rt_throttled = 0。
4.總結
上面簡要分析了內核中如何通過帶寬限制來防止實時任務無限制的占用cpu資源的原理,現將實現方式簡單歸納如下:
(1)帶寬限制的對象是一個組,組內的實時任務都掛到同一個運行隊列rt_rq上,最終帶寬限制的實施對象就是rt_rq;
(2)內核在多個關鍵點更新任務的信息update_curr_rt(),而更新當前任務信息后,就會檢查組的運行時間是否超過帶寬限制;
(3)如果一個rt_rq超過帶寬限制,則會標記此rt_rq調度受限,此后rt_rq上的實體將被移出隊列,并且帶寬限制解除前無法再加入到隊列上;
(4)每個任務組都維護著一個高精度時鐘rt_period_timer用以定期(rt_period)更新rt_rq上的運行時間,并對"被懲罰到位"的rt_rq解除調度限制。
(5)一個典型的Android系統,定時器的周期是1s,實時進程的最大可運行時間是0.95s。
綜上,進程組的實時進程帶寬限制原理框圖總結如下:
總結
以上是生活随笔為你收集整理的RT进程组的cpu带宽限制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 鼠标指针乱跑的解决方案
- 下一篇: 【分享】翻出过去的一个多彩泡泡屏保特效(