HMP调度器和EAS调度器

HMP调度器

为了降低功耗,ARM开发了大小核架构处理器。Linux内核中的负载均衡算法基于SMP模型,并未考虑big.LITTLE模型,因此Linaro开发了一个HMP调度器用于支持这种架构,它也被用于Android 5.x和Android6.x中,但这种调度器并没有被合入内核的基线中。
该调度器的进程调度算法基本上和CFS一样,主要区别在于调度域和负载均衡的处理上。HMP调度域的实现比自带的CFS调度器要简单的多,首先它只包含两个调度域,大核调度域和小核调度域,也不考虑这两个调度域之间的负载均衡问题,没有调度组和调度能力的概念,调度域也没有拓扑层次关系。它的主要工作原理是:
(1)检测小核调度域,如果有繁重的任务(最重的),就迁移到大核调度域中的空闲CPU上(idle CPU)。
(2)检测大核调度域,如果有简单的任务(最轻的),就迁移到小核调度域中的空闲CPU上(idle CPU)。

它的主要复杂点就是如何界定这里的繁重任务和简单任务,HMP使用load_avg_ratio来表示进程负载的轻重:

load_avg_ratio = runnable_avg_sum * 1024 / runnable_avg_period

这和CFS中的默认算法是有区别的,这里的进程负载不再于各进程的权重有关了,而只和两个参量相关。
可迁移的大任务对应的负载要高于:hmp_up_threshold = 700 (runnable_avg_sum / runnable_avg_period > 68.3%)
可迁移的小任务对应的负载要低于:hmp_down_threshold = 512 (runnable_avg_sum / runnable_avg_period < 50%)

EAS调度器

CFS调度器和SMP负载均衡主要是为了性能优先的场景,把负载均衡在每个CPU上发挥每个CPU的能力,对于功耗的处理没有那么好,而EAS就是为了嵌入式设备而设计,在保证性能的前提下竟可能的节省功耗,目前它已经逐渐替代HMP调度器,并且在Android 7.x之后开始使用。
EAS调度器新增了能效模型到调度器中,所谓能效也就是CPU的运行能力和对应的功耗,在内核中都会进行量化处理,内核中用capacity表示CPU能力,而功耗用power表示功耗。在调度域初始化时会读取dts中配置的能效数据,所谓能效是指,不同的P-state和C-state对应的CPU计算能力和CPU功耗量化值。
EAS调度器与CFS调度器主要的差异点:
(1)CFS调度器中就已经有capacity的概念了,在EAS调度器中对capacity的计算更加精细,系统中能够定义的capacity最大值为1024。

cpu_capacity_org = cpu_max_capacity_scale * cpu_max_freq / system_max_freq

这里的cpu_max_capacity_scale表示dts中传入的当前CPU最大能力值,cpu_max_freq指当前CPU最大频率值,system_max_freq表示系统中CPU最大频率值。这样就把cpu_capacity_org和cpufreq联系在了一起,另外这里表示的是整个CPU的capacity,CFS runqueue的capacity还要进一步减去rt和dl调度类的capacity。

(2)cfs运行队列runnable_load_sum负载的计算方法和PELT算法基本上一致,重要的区别就是进程负载加入了对CPU freq和CPU capacity的考虑。EAS调度器为什么还要计算CFS使用的负载呢?因为后面会讲到,当判断CPU处于over-utilized状态时,会触发CFS负载均衡操作,这个操作都是使用此负载值的。
(3)新增WALT算法计算WALT运行时间,以此作为依据获取下一个时间窗口预计的运行时间,这个运行时间的作用是为了在进程唤醒时根据需要的CPU能力查找合适的运行CPU。时间窗口默认是20ms,这里注意和PELT中period的区分。WALT运行时间是在一个时间窗口内,进程处于runable状态的时间,通过公式换算得出的,最大不超过一个时间窗口(默认20ms)。
计算公式为:

walt_time = delta_exec_time * (curr_freq / system_max_freq) * (curr_IPC / system_max_IPC)

delta_exec_time是实际运行的物理时间,curr_freq指当前CPU的频率,system_max_freq系统最大频率,curr_IPC当前CPU的计算效率,system_max_IPC系统最大的CPU计算效率。这个公式包含了运行频率因素,同时也包含了大小核因素。在一个时间窗口内的walt_time时间保存到ravg.sum中,同时也会保留最近5个的历史数据到ravg.sum_history[5],WALT算法会默认使用最近值和平均值两者最大值作为demand,这个就是预测的下一个时间窗口需要的walt运行时间。

(4)WALT与PELT还有一个重要的差别,就是WALT在计算walt_time时是不统计睡眠时间的,这样就忽略了睡眠时间对于负载的影响,这是与PELT关键的一个差异。
(5)唤醒进程时会判断当前wakeup CPU是否负载过重,如果是over-utilized,那么执行CFS负载均衡,否则负载不重,执行EAS的算法选择CPU进行执行。
a.查找合适的调度组

capacity = demand * 1024 / walt_ravg_window

查找合适调度组,计算得出一个capacity值,需要找到一个调度组计算能力要达到期望能力的125%才会被选择,并且是所有调度组中最小的调度组。
b.查找合适的CPU

(demand + pre_runnable_sum) * 1024 / walt_ravg_window < CPU capacity

进一步筛选CPU,这次除了demand,还需要把pre_runnable_sum也要加上进行计算。
c.找到合适的target CPU后,还有一个重要的步骤,就是计算energy,对比进程迁移前后的energy值大小,如果进程迁移到target CPU后energy反而增加,那么就保持不变。
(6)新增CPU调频策略“sched”,在EAS调度器中获取负载相关的信息,并且执行对应的调频操作,增强CPUfreq和scheduler之间的联系。

你可能感兴趣的:(内核笔记,进程管理)