RT throttling分析【轉】


轉自:https://blog.csdn.net/u012728256/article/details/72639612

Linux上調度策略為SCHED_FIFO的實時進程是根據優先級搶占運行的。當沒有更高優先級的實時進程搶占,而此進程又由於bug等原因長時間運行,不調度其它進程,系統就會出現無響應。這里要分析的RT throttling就是針對此種情況的,它通過限制每個單位時間內分配給實時進程的CPU運行時間,來防止上述情況的出現。

 

 

標准的設置是1s的時間內,實時進程的運行時間是950ms,其余的50ms時間給normal進程使用。

 

sched_rt_period_us值為1000000us=1s,表示單位時間為1s

sched_rt_runtime_us值為950000us=0.95s,表示實時進程的運行時間為0.95s。

這兩個接口的實現代碼如下:

Kernel/sysctl.c的kern_table片段

{  

    .procname = "sched_rt_period_us", 

    .data   = &sysctl_sched_rt_period

    .maxlen   = sizeof(unsigned int), 

    .mode   = 0644, 

    .proc_handler = sched_rt_handler, 

},  

{  

    .procname = "sched_rt_runtime_us", 

    .data   = &sysctl_sched_rt_runtime,                                                                                                     

    .maxlen   = sizeof(int), 

    .mode   = 0644, 

    .proc_handler = sched_rt_handler, 

},  

sched_rt_period_us接口設置的是sysctl_sched_rt_period變量,sched_rt_runtime_us接口設置的是sysctl_sched_rt_runtime變量。讀寫的實現都是通過sched_rt_handler函數,這里就不具體分析了。

 

 

我們知道了如何設置RT throttling,那么它是如何工作的呢?在實時進程的運行時間超出設定的閾值是如何處理的?

static void update_curr_rt(struct rq *rq) 

{    

    if (curr->sched_class != &rt_sched_class) /*判斷當前進程調度類*/

        return; 

    /*運行隊列現在的時間與當前進程開始運行時間之差* */

    delta_exec= rq_clock_task(rq)-  curr->se.exec_start;    

    curr->se.sum_exec_runtime +=  delta_exec;  /*更新進程的真實運行時間*/                                                     

    curr->se.exec_start =  rq_clock_task(rq); 

  

    if (!rt_bandwidth_enabled())  /*判斷RT throttling是否開啟*/

        return;

 

    for_each_sched_rt_entity(rt_se) { /*/*遍歷此實時進程的調度單元*/*/

        struct rt_rq*rt_rq =rt_rq_of_se(rt_se); 

 

        if (sched_rt_runtime(rt_rq)!=  RUNTIME_INF) { 

            rt_rq->rt_time +=  delta_exec;

            /*rt_rq的運行時間是否超過了分配給它的時間片*/   

            if (sched_rt_runtime_exceeded(rt_rq))

                resched_task(curr); 

            }  

        }  

    }  

update_curr_rt函數用來更新當前實時進程的運行時間統計值,如果當前進程不是實時進程,即調度類不為rt_sched_class,則直接返回。

delta_exec值為此運行隊列的當前時間與此進程開始運行時間之差,也即是此進程此次調度運行的時長。然后更新進程的真實運行時間和開始運行時間。

rt_bandwidth_enabled函數判斷sysctl_sched_rt_runtime變量值是否大於0,如果此變量值設置為RUNTIME_INF(很大的負數),就關掉了RT throttling功能,這里就會直接返回。

然后遍歷此實時進程的調度實體,找到相應的就緒隊列,更新運行時間后,通過sched_rt_runtime_exceeded函數判斷是否此實時進程是否超過了分配給它的時間片。

 

 

sched_rt_runtime_exceeded代碼片段:

    u64 runtime =sched_rt_runtime(rt_rq);   /*獲取當前隊列的最大運行時間*/                                                  

  

    if (rt_rq->rt_throttled)    /*當前隊列的實時調度受到限制*/     

        return rt_rq_throttled(rt_rq); 

    /*當前隊列的最大運行時間大於當前隊列的調度周期時間*/

    if (runtime>= sched_rt_period(rt_rq))  

        return 0; 

  

    balance_runtime(rt_rq); 

    runtime= sched_rt_runtime(rt_rq);  /*重新獲取當前隊列的最大運行時間*/

    if (runtime== RUNTIME_INF) /*關閉了RT throttling*/

        return 0;  

runtime值為當前隊列的最大運行時間rt_runtime。rt_throttled字段表示當前隊列的實時調度是否受到限制,如果受到限制了,就直接返回1,在update_curr_rt函數中就會調用resched_task函數執行進程切換,讓出cpu。

如果當前隊列的最大運行時間大於當前隊列的調度周期時間,則返回0,這樣此運行隊列上的任務還能夠繼續運行。

balance_runtime函數在RT_RUNTIME_SHARE特性使能的情況下,如果當前隊列的運行時間超過了最大運行時間,則可以從其他cpu上借用時間。具體代碼這里先不分析,后面分析。

重新獲取當前隊列的最大運行時間runtime,如果值等於RUNTIME_INF說明關閉了RT throttling,則直接返回0。

 

 

sched_rt_runtime_exceeded代碼片段:

    if (rt_rq->rt_time >  runtime) {  /*累計運行時間大於最大運行時間*/                                                                     

        struct rt_bandwidth *rt_b =sched_rt_bandwidth(rt_rq); 

 

        if (likely(rt_b->rt_runtime)) { 

            rt_rq->rt_throttled =  1; 

            printk_deferred_once("sched: RT throttling activated\n"); 

        } else {  

            rt_rq->rt_time =  0; 

        }  

 

        if (rt_rq_throttled(rt_rq)) { /*檢查隊列的實時調度是否受到限制*/

            sched_rt_rq_dequeue(rt_rq); /*將調度實體從實時運行隊列中刪除*/

            return 1; 

        }  

    }   

如果累計運行時間大於最大運行時間,就會執行上面的代碼片段。rt_b為運行隊列rt_rq的進程組帶寬控制結構體指針,如果rt_runtime即此進程組的任務運行時間額度值有效,則設置rt_throttled為1,表明此隊列的實時調度受到限制,並打印出“sched: RT throttling activated”信息。接着檢查隊列的實時調度如果受到限制,則返回1,在update_curr_rt函數中讓出cpu。

 

 

在前面講到balance_runtime在當前隊列運行時間超過最大運行時間后,可以從其他cpu上借用時間,下面具體分析代碼看下是如何實現的。

static int balance_runtime(struct rt_rq *rt_rq) 

{  

    if (!sched_feat(RT_RUNTIME_SHARE))   /*RT_RUNTIME_SHARE支持多個cpu間的rt_runtime共享*/                     

        return more; 

 

    if (rt_rq->rt_time >  rt_rq->rt_runtime) {  

        raw_spin_unlock(&rt_rq->rt_runtime_lock);  

        more =do_balance_runtime(rt_rq); 

        raw_spin_lock(&rt_rq->rt_runtime_lock);  

    }  

}  

RT_RUNTIME_SHARE默認是使能的(見kernel/sched/features.h文件)

SCHED_FEAT(RT_RUNTIME_SHARE, true)

它表示支持多個cpu間的rt_runtime共享。如果不支持的話,就直接返回。

如果當前隊列的累計運行時間大於最大運行時間,則調用do_balance_runtime函數。

 

 

do_balance_runtime函數代碼:

    struct rt_bandwidth *rt_b =sched_rt_bandwidth(rt_rq); 

    struct root_domain *rd =rq_of_rt_rq(rt_rq)->rd;  

  

    weight= cpumask_weight(rd->span);  

  

    rt_period =ktime_to_ns(rt_b->rt_period);  /*任務組一個控制周期的時間*/                                                                      

    for_each_cpu(i,rd->span) {  

        /*找到在另一個cpu上運行的同一任務組的運行隊列*/

        struct rt_rq *iter =sched_rt_period_rt_rq(rt_b,i); 

  

        if (iter== rt_rq) /*同一運行隊列則跳過*/

            continue; 

   

        if (iter->rt_runtime ==  RUNTIME_INF) /*RT throttling關閉,不允許借用時間*/

            goto  next;  

  

        diff =iter->rt_runtime -iter->rt_time;  /*最大能夠借用時間*/

        if (diff> 0) { 

            diff =div_u64((u64)diff,weight); 

            if (rt_rq->rt_runtime +  diff> rt_period) 

                diff =rt_period -  rt_rq->rt_runtime;  /*修正后可借用*/

            iter->rt_runtime -=diff; 

            rt_rq->rt_runtime +=diff; 

            more =1; 

            if (rt_rq->rt_runtime ==rt_period) {/*滿足條件退出,否則繼續從其他cpu借用*/

                break; 

        }  

    }  

next:  

}  

 

 

rd->span表示此調度域的rq可運行的cpu的一個mask,這里會遍歷此mask上的cpu,如果對應的cpu的rq和當前的rq是同一運行隊列,則直接跳過;如果對應的cpu的rq已關閉RT throttling功能,則不允許借用時間。內核中關於這塊代碼的注釋是:

Either all rqs have inf runtime and there's nothing to steal or __disable_runtime() below sets a specific rq to inf to indicate its been disabled and disalow stealing.

大概意思是如果所有的運行隊列都設置為RUNTIME_INF即關閉了RT throttling功能,則沒有時間可以借用。或者某個指定的運行隊列調用__disable_runtime()函數,則不允許別的借用自己的時間。

 

 

diff是iter運行隊列最大能夠借用的時間,后面經過修正后,將diff加入到rt_rq的最大可運行時間上。如果新的最大可運行時間等於此任務組的控制周期的時間,則不需要接着再從其他的CPU上借用時間,就直接break退出。

 

 

實時進程所在的cpu占用超時,可以向其他的CPU借用,將其他CPU的時間借用過來,這樣此實時進程所在的CPU占有率達到100%,這樣做的目的是為了避免實時進程由於缺少CPU時間而向其他的CPU遷移,減少不必要的遷移成本。此cpu上為綁定核的普通進程可以遷移到其他cpu上,這樣就會得到調度。但是如果此CPU上有進程綁定核了,那么就只有在這里餓死了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM