Linux系统主调度函数,当执行到该函数时,从当前cpu的rq队列中选择一个task投入运行:


static void __sched__schedule(void) {
	struct task_struct *prev, *next;
	...
	rq = cpu_rq(cpu);
	...
	put_prev_task(rq, prev);
	...
	next = pick_next_task(rq);
	...
	context_switch(rq, prev, next);
}




Kernel仓库 kernel group_Kernel仓库


1, CFS组调度


fair_sched_class.pick_next_task = pick_next_task_fair




Kernel仓库 kernel group_红黑树_02



pick_next_entity从系统cfs队列(csf_rq)中选择一个调度实体se(红黑树最左边的节点);


set_next_entity设置cfs_rq->curr = se 等;


group_cfs_rq会判断选择到的se是对应一个task还是一个task_group,如果是task可直接切换到该task运行,如果是task_group还需要继续往下执行。可根据se->my_q判断se对应什么,如果se->my_q为空表示se是一个task,如果不为空se表示是一个task_group(调度组):


/* runqueue "owned" by this group */ 

 
group_cfs_rq(struct sched_entity *grp) { 

 

  return grp->my_q; 

 

  }


如果调度实体代表task_group,则它的my_q字段指向这个调度组对应的运行队列;否则my_q字段为NULL,调度实体代表task。


Kernel仓库 kernel group_Kernel仓库_03



所以如果group_cfs_rq判断se是调度组,那么该调度组自己的cfs队列就是cfs_rq = group_cfs_rq(se)。下次循环就从调度组自己的cfs队列中选择一个调度实体se(红黑树最左边的节点),如果se还是对应调度组,就继续递归下去,直到在某个子(后代)调度组的cfs队列中找到对应一个task的se,然后将其投入运行。



一个组调度对应内核要为其创建一个task_group。例如,在系统CPU子系统/sys/fs/cgroup/cpu/下面创建一个目录my_test,即/sys/fs/cgroup/cpu/my_test,然后将系统中的某些进程转移到my_test中去,echo PIDxxx > /sys/fs/cgroup/cpu/my_test/tasks。 内核将为你的my_test cpu子系统创建一个task_group用于my_test的组调度, 并为该task_group初始化它自己的se与cfs,rt队列(为task_group结构体的变量),如下代码所示:



Kernel仓库 kernel group_Kernel仓库_04



为my_test创建struct task_group:






Kernel仓库 kernel group_初始化_05



初始化struct task_group的cfs_rq队列与cfs_se(用于组中NORMAL进程的调度):





Kernel仓库 kernel group_Kernel仓库_06



初始化struct task_group的rt_rq队列与rt_se(用于组中实时进程的调度):



Kernel仓库 kernel group_红黑树_07





初始化结果(假设系统有两个CPU):



Kernel仓库 kernel group_字段_08





例如,在my_test下又创建了my_test_child,


/sys/fs/cgroup/cpu/my_test =>对应一个task_group 

 

  /sys/fs/cgroup/cpu/my_test/my_test_child =>对应一个task_group 

 

  echo PIDxxx > /sys/fs/cgroup/cpu/my_test/my_test_child/tasks


其在CPU0上的调度过程如下:


从cpu_rq(0)->cfs_rq中得到红黑树最左侧节点se --->


如果该se是my_test对应task_group->se[0], 那么se[0]->my_q != null,说明是个组调度实体, 从该组调度实体相应队列se[0]->my_q中获取红黑树最左侧节点se --->


如果该se是my_test_child对应task_group->se[0],如果se[0]->my_q != null,说明仍然是个组调度实体, 继续从该组调度实体相应队列se[0]->my_q中获取红黑树最左侧节点se --->


se[0]->my_q == null, 说明se是个task,其PID正是PIDxxx ,然会返回给__schedule函数投入调度。



2, rt组调度


rt_sched_class.pick_next_task = pick_next_task_rt





Kernel仓库 kernel group_初始化_09






Kernel仓库 kernel group_红黑树_10