RT throttling分析

来源:互联网 发布:手机影子软件 编辑:程序博客网 时间:2024/06/03 08:35

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.ckern_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字段表示当前队列的实时调度是否受到限制,如果受到限制了,就直接返回1update_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_throttled1,表明此队列的实时调度受到限制,并打印sched: RT throttling activated”信息。接着检查队列的实时调度如果受到限制,则返回1update_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)

表示支持多个cpurt_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这里会遍历此maskcpu如果对应的cpurq当前的rq同一运行队列,则直接跳过;如果对应的cpurq关闭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()函数,则不允许别的借用自己的时间。


diffiter运行队列最大能够借用的时间,后面经过修正后,将diff加入rt_rq的最大可运行时间上。如果新的最大可运行时间等于任务组的控制周期的时间,则不需要接着再从其他的CPU上借用时间,就直接break退出。


实时进程在的cpu占用超时,可以向其他的CPU借用将其他CPU的时间借用过来,这样此实时进程所在的CPU占有率达到100%这样做的目的是为了避免实时进程由于缺少CPU时间而向其他的CPU迁移,减少不必要的迁移成本。cpu上为绑定普通进程可以迁移到其他cpu上,这样就会得到调度。但是如果CPU上有进程绑定核了,那么就只有在这里饿死




原创粉丝点击