PELT任务负载跟踪
为了实现sched entity级别的负载跟踪,pelt将物理时间划分成了1ms(实际为1024us)的序列,在每一个1024us的周期中,sched entity对系统负载的贡献可以根据该entity处于runnable状态(包含在cpu上running和在cfs_rq上waiting的状态)的时间进行计算;假设该周期内,某entity处于runnable状态的时间为x,那么其对系统负载的贡献为(x/1024);pelt还会累积过去周期的负载贡献,但会根据时间乘以相应的衰减系数y,假设Li表示某entity在周期Pi中对系统负载的贡献,那么该entity对系统负载的总贡献如下:
每经过32个周期,entity对系统负载的贡献减半。
Linux调度器负载计算之PELT_accumulate_sum(u64 delta, struct sched_avg *sa, un-CSDN博客 调度实体负载计算方法
Ln = (prev+deltal1)*y^p + 1024(y + y^2 + y^3 + .... + y^p-1) + delta3
s1 = (prev+deltal1)*y^p,
s2 = 1024(y + y^2 + y^3 + .... + y^p-1)
s3 = deltal3
s2 = 1024*(y^1 + y^2 + ... +y^p-1)
s2a= (y^0 + y^1+y^2 + ... + y^n)
s2b = (y^p + y^p+1 + ..... y^n)=s2a*y^p
s2 = 1024(s2a - s2b - y^0) = 1024(s2a-s2b-1024)= 1024*(s2a - s2a*y^p - 1)
利用Sn = (a1 *(1-q^n))/(1-q)当前n趋于无穷时等于 1 /(1- Y)
求s2a的最大值LOAD_AVG_MAX =1024*s2a = 1 /(1- Y) * 1024 = 47742
在___update_load_avg中使用这个公式进行负载的累积计算;在这个函数中同时更新se和对应的cfs衰减后的值。
计算后的值更新在se的成员结构体sa中:
struct sched_avg {
//上一次负载更新的时候,通过这个值和当前值的差,计算delta;对应上图中的last update time
u64 last_update_time;
//now时间点的负载总和,也就是下一次计算时的prev值;按照weight计算的累加值
u64 load_sum;
// 按照频率最大为1024进行归一化计算的结果。
u32 util_sum;
// 图上1024 -delta1的值
u32 period_contrib;
// load_sum / LOAD_AVG_MAX
unsigned long load_avg;
// util_sum / LOAD_AVG_MAX
unsigned long util_avg;
};
通过上面的算法计算出当前任务的contrib,然后分别 结算出load_sum和util_sum值。
sa->load_sum += weight * contrib;
sa->util_sum += contrib * scale_cpu;
CPU负载跟踪
基于上面的公平运行队列的加权平均负载,计算5种处理器负载,计算公式如下:
其中i的取值是0~4,load_avg是根任务组的公平运行队列的加权平均负载。
5种负载的区别是,历史负载和当前负载的比例不同,i越大,历史负载占的比例越大,处理器负载曲线越平滑。在处理器不空闲、即将空闲和空闲等不同情况下实现负载均衡时,使用不同的处理器负载。
在find_idlest_group和update_sg_lb_stats的时候会使用到这个值。也就是说这个值在负载均衡的时候用到。
选择下一个进程
停机调度类中用于选择下一个进程的函数是picknext_task_stop,算法是:如果运行队列的成员stop指向某个进程,并且这个进程在运行队列中,那么返回成员stop指向的进程,否则返回空指针。
限期调度类中用于选择下一个进程的函数是pick_next_task_dl,算法是:从限期运行队列选择绝对截止期限最小的进程,就是红黑树中最左边的进程。限期调度类不支持任务组,所以不需要考虑调度实体是任务组的情况。
实时调度类中用于选择下一个进程的函数是pick_nexttaskrt,算法如下。
- 如果实时运行队列没有加入运行队列(rt_rq.rt_queued等于0,如果在一个处理器上所有实时进程在一个周期内用完了运行时间,就会把实时运行队列从运行队列中删除),那么返回空指针。
- 从根任务组在当前处理器上的实时运行队列开始,选择优先级最高的调度实体。
- 如果选中的调度实体是任务组,那么继续从这个任务组在当前处理器上的实时运行队列中选择优先级最高的调度实体,重复这个步骤,直到选中的调度实体是进程为止。
公平调度类中用于选择下一个进程的函数是picknext_task_fair,算法如下。
- 从根任务组在当前处理器上的公平运行队列中,选择虚拟运行时间最小的调度实体,就是红黑树中最左边的调度实体。
- 如果选中的调度实体是任务组,那么继续从这个任务组在当前处理器上的公平运行队列中选择虚拟运行时间最小的调度实体,重复这个步骤,直到选中的调度实体是进程为止。