调度周期:预期多长时间会执行一次
时间片:在调度周期内分配的运行时间
/proc/sys/kernel 下面有对应的时间
调度延迟:从挂入队列到获得cpu资源的等待时间
最小调度粒度:在调度周期内保证的最小运行时间
每个cpu rq 对应cfs_rq 和rt_rq
ps -AT -o cmd,pid,sched,nice,rtprio,pri
查看调度信息
scheduler中的RT调度器,其中有看到一个cpupri,作为选核的一部分,
暂把一个cpu中正在排队以及正在running的所有rt task最高的优先级叫做此cpu的优先级。
根据这个优先级可以对CPU进行排序,
对于RT调度器来讲,其重要任务是保证优先级高的task优先执行,优先级高的task的响应时间也要最快。
假定一个task上面已经有比较高优先级的Task在排队或者run的话,这时候一个就绪的优先级比较低的task加入就得等待,
那么这个task 就会排队,造成runnable,即便是其他cpu上面所有task的优先级都比这个task还低。这样显然不合理。
而cpupri则负责跟踪记录各个cpu中所有task的最高优先级情况,方便后面寻找优先级最低的cpu出来,供RT调度器进行选择。
kernel-4.19/kernel/sched/cpupri.h
1
2
3 #define CPUPRI_NR_PRIORITIES (MAX_RT_PRIO + 2)
4
5 #define CPUPRI_INVALID -1
6 #define CPUPRI_IDLE 0
7 #define CPUPRI_NORMAL 1
8
9
10 struct cpupri_vec {
11 atomic_t count;
12 cpumask_var_t mask;
13 };
14
15 struct cpupri {
//有多少个RT优先级就有多少个元素。每个元素记录吧这个优先级分别是哪些个cpu的最高优先级。所以理论上讲最多只有8个元素是有值的,假定有8个cpu的话。
//102个元素,见convert_prio(),invalid:-1,idle:0,cfs:1,rt:101-prio(0-99)
16 struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES];
//一个有多少个cpu就有多少个元素的数组。每个元素代表对应cpu中当前最高优先级的值。
17 int *cpu_to_pri;
18 };
19
20 #ifdef CONFIG_SMP
21 int cpupri_find(struct cpupri *cp, struct task_struct *p,
22 struct cpumask *lowest_mask);
23 int cpupri_find_fitness(struct cpupri *cp, struct task_struct *p,
24 struct cpumask *lowest_mask,
25 bool (*fitness_fn)(struct task_struct *p, int cpu));
26 void cpupri_set(struct cpupri *cp, int cpu, int pri);
27 int cpupri_init(struct cpupri *cp);
28 void cpupri_cleanup(struct cpupri *cp);
29 #endif
RT进程唤醒选核
1. 选择运行优先级最低cpu;
2. 选的核上如果curr优先级比唤醒的低,则会设置TIF_NEED_RESCHED
kernel-4.19/kernel/sched/sched.h
struct root_domain {
786
790 cpumask_var_t rto_mask;
791 struct cpupri cpupri;
792
793
794 struct max_cpu_capacity max_cpu_capacity;
795
}
static int init_rootdomain(struct root_domain *rd){
512 init_dl_bw(&rd->dl_bw);
513 if (cpudl_init(&rd->cpudl) != 0)
514 goto free_rto_mask;
515
516 if (cpupri_init(&rd->cpupri) != 0)
517 goto free_cpudl;
518
519 init_max_cpu_capacity(&rd->max_cpu_capacity);
}
268
274 int cpupri_init(struct cpupri *cp)
275 {
276 int i;
277
278 for (i = 0; i < CPUPRI_NR_PRIORITIES; i++) {
279 struct cpupri_vec *vec = &cp->pri_to_cpu[i];
280
281 atomic_set(&vec->count, 0);
282 if (!zalloc_cpumask_var(&vec->mask, GFP_KERNEL))
283 goto cleanup;
284 }
285
286 cp->cpu_to_pri = kcalloc(nr_cpu_ids, sizeof(int), GFP_KERNEL);
287 if (!cp->cpu_to_pri)
288 goto cleanup;
289
290 for_each_possible_cpu(i)
291 cp->cpu_to_pri[i] = CPUPRI_INVALID;
292
293 return 0;
294
295 cleanup:
296 for (i--; i >= 0; i--)
297 free_cpumask_var(cp->pri_to_cpu[i].mask);
298 return -ENOMEM;
299 }
idle位于0元素,而normal task位于1元素,而后才是按照rt优先级排序的元素。
实际上由于只有rt调度器当中才会调用cpupri_set。所以,对于idle与normal来讲都是位于1元素位置,
75 void init_rt_rq(struct rt_rq *rt_rq)
76 {
77 struct rt_prio_array *array;
78 int i;
79
80 array = &rt_rq->active;
81 for (i = 0; i < MAX_RT_PRIO; i++) {
82 INIT_LIST_HEAD(array->queue + i);
83 __clear_bit(i, array->bitmap);
84 }
85
86 __set_bit(MAX_RT_PRIO, array->bitmap);
87
88 #if defined CONFIG_SMP
89 rt_rq->highest_prio.curr = MAX_RT_PRIO;
90 rt_rq->highest_prio.next = MAX_RT_PRIO;
91 rt_rq->rt_nr_migratory = 0;
92 rt_rq->overloaded = 0;
93 plist_head_init(&rt_rq->pushable_tasks);
94 #endif
95
96 rt_rq->rt_queued = 0;
97
98 rt_rq->rt_time = 0;
99 rt_rq->rt_throttled = 0;
100 rt_rq->rt_runtime = 0;
101 raw_spin_lock_init(&rt_rq->rt_runtime_lock);
102 }
103
1、try_to_wake_up 唤醒
2、select_task_rq_rt 选核
3、find_lowest_task_rt 确定最低优先级
当一个任务被唤醒时候,需要根据任务的类型:ui,task_demand_fit, boost等,确定起始的调度域,如ui可能优先从中核开始调度;
根据任务特性选择是否走快速路径,如当前任务马上就要执行结束,满足一定条件将唤醒进程放在当前或者唤醒进程的prev_cpu上;
根据能耗比,选择idle cpu,或者当前空闲算力最大的cpu;
根据线程类型,进一步修正如,ui选核时候会优先从cpu_cap 大的往小的选;除非当前选核时 候大核超大核全是pri 设置重新调度的标志位: 1. 分配时间片用完; 2. 分配时间片没用完但已经保证了最小调度粒度,当前的队列中有虚拟时间更小的任务; 3. set_next_buddy: 唤醒的线程虚拟时间大于curr一个时间片; pick_next_task_rt 选择任务 判断当前进程运行结束后下一个进程的优先级比当前优先级低,就会触发一次pull_rt_task的操作; 寻找rt_rq的最高优先级,通过位图确认; 选择list 头的任务 每个优先级对应一个列表,里面是属于该优先级的任务,从而根据这些信息选择优先级最高任务执行。 红黑树以虚拟时间为key值,每次选择虚拟时间最小的task_struct来执行;如果开启了next_buddy的feature,则next满足条件会抢占left执行; 2. 普通进程的优先级影响是虚拟时间的计算; 3. 优先级影响了将实际时间片转化为虚拟时间片的比例; 4. 虚拟时间片最终影响(例外:next_buddy, ui)选择某个进程 频率影响性能与功耗 P = C * F * V2
在调频的时候一般需要增加电压,当频率增加,功耗增加更加明显;



