linux-cpu 调度

  • Post author:
  • Post category:linux


调度域与调度组(CFS 负载均衡)

根域(ld/rt 负载均衡)


腾讯云原生:Linux 内核调度器源码分析 – 初始化 _ 【IIS7站长之家】


Linux内核调度器源码分析 – 初始化


调度周期:预期多长时间会执行一次


时间片:在调度周期内分配的运行时间

/proc/sys/kernel 下面有对应的时间


调度延迟:从挂入队列到获得


cpu


资源的等待时间


最小调度粒度:在调度周期内保证的最小运行时间

每个cpu rq 对应cfs_rq 和rt_rq

ps -AT  -o  cmd,pid,sched,nice,rtprio,pri

查看调度信息

RT


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  /* SPDX-License-Identifier: GPL-2.0 */

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  /* values 2-101 are RT priorities 0-99 */

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      /*

787       * The “RT overload” flag: it gets set if a CPU has more than

788       * one runnable RT task.

789       */

790      cpumask_var_t        rto_mask;

791      struct cpupri        cpupri;

792

793      /* Maximum cpu capacity in the system. */

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  /**

269   * cpupri_init – initialize the cpupri structure

270   * @cp: The cpupri context

271   *

272   * Return: -ENOMEM on memory allocation failure.

273   */

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      /* delimiter for bitsearch: */

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 /* CONFIG_SMP */

95      /* We start is dequeued state, because no RT tasks are queued */

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<MAX_RT_PRI;ui线程会选大核


设置重新调度的标志位:


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

在调频的时候一般需要增加电压,当频率增加,功耗增加更加明显;



版权声明:本文为lei7143原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。