作者:新浪微博(@NP等不等于P) 计算机学习微信公众号(jsj_xx) 对于朴素的CFS调度而言,其控制粒度是一个进程,这样的粒度在很多场景是不合适的。比如,希望达到用户粒度,也就是希望每个用户占有相同的cpu时间。但是,在各个用户拥有的进程数量不同的情况下,显然朴素的CFS(请参考我们之前的《理解进程CFS调度》)会将cpu更多地分配给进程数量多的用户。组调度的引入,正是解决此问题的。 参考linux kernel souce code 4.0,本文分享我们(计算机学习微信公众号:jsj_xx)对组调度的理解(如有错误,还望指正,谢谢)。 组调度其实属于cgroup框架的cpu子系统,故需要开启cgroup:CONFIG_CGROUP_SCHED。同时,开启CFS组调度:CONFIG_FAIR_GROUP_SCHED。注意,本文仅讨论普通进程,不涉及实时进程。 1 组调度原理 我们先看task_group(本文中简称为tg)的shares的含义。 tg的shares值就是该tg(se)的权重。想想CFS中的进程,其权重需要从优先级转化而来,而tg就简单了:直接指定即可。另外,tg和task是两种完全不同的个体,由此引入se来抽象两者:tg和task。 以下是我对组调度(tg)的理解要点:
组调度的原理,就是每个tg都拥有自己的cfs_rq以及融入上级的se,从而搭建出一颗se/cfs_rq树,通过tg的shares或者task的权重将所有节点(tg或task)管理起来。关键是,引入了平行于task的调度实体:tg。 2 tg数据结构 我们看下tg的数据结构:
简单地说,就是搭建了两颗树:tg/cgroup固定树和se/cfs_rq灵活树。 3 与cgroup的关系 一个tg,通过内嵌一个css(cgroup_subsys_state)的方式融入到cgroup框架里(cgroup框架,我们以后会有专题去谈)。涉及到cgroup中的cpu子系统,相关的注册函数如下: 我们主要看看cpu_cgroup_css_alloc(),创建css(也就是tg,因为css是内嵌于tg里的)的处理。 对于顶级tg:root_task_group,其css是预留的,无需分配。对于普通tg,需要分配tg(和内嵌的css)。这样,cgroup/tg的树结构层次,也就在这个处理中搭建出来了。 【参考cpu_cgroup_css_alloc()->sched_create_group()->alloc_fair_sched_group()】 另外,还可以看到,task加入tg的动作是支持批处理的。 【参考cpu_cgroup_attach()】 我们再看看设置shares时的处理,这是借助cgroup文件系统里的shares文件来实现的,其处理函数: 在sched_group_set_shares()里设置指定tg的shares值: 此处是组调度的核心处理,设置tg的shares。我们仔细分析此函数:
理解了这些,我们重新审视此tg的shares:
回到sched_group_set_shares(),继续分析。此处理中,最关键的是两个要点:
那么,修改tg的shares时,如果不扩散呢? 我的理解是:tg的shares的修改,如果不做上述这种扩散处理,甚至不去利用各个tg在各个cpu上的load比重,这样,tg的shares在各个cpu上都是一样的(都等于tg的shares值),也是可以保证组调度的运行。但是,这种实现仅能保证了tg的粒度,tg内的task粒度却失衡了。。。 最后,要补充一下上述讨论的前提:各个cpu是负载均衡的。抛开均衡(SMP负载均衡涉及调度域,我们以后再谈)谈组调度是没有意义的,我觉得。毕竟tg的shares是一个值,而调度时所有cpu都是围绕该值运转,如果cpu负载不均,势必令调度失去公平。 4 与CFS的关系 CFS的红黑树存在于每一个tg里,也就是说每个tg的cfs_rq都在运作一个CFS调度(参考我们之前的《理解进程CFS调度》)。这样,在将一个task加入到某个tg时,需要自底(表示叶子的task)向上层次地做入队列处理: 自然,需要保证从该task到根节点是完整的路径,所以这个上溯动作直到碰到某层的tg(se)早已经存在于队列里才停止。 出队列也是如此。 task出队列,则沿着task所在的tg上溯,将沿途的只有一个成员的tg也做出队列操作。可见,上溯出队列操作会持续到碰到一个tg,该tg至少包含一个跟此task无关(此处的“无关”是指此成员不在此上溯路径里)的成员。 注意,上述两个操作都是在se/cfs_rq树中。 至于各个(层)的tg或task,它们CFS调度涉及的vruntime,这些都是独立的。也就是说,层次之间的CFS运转是独立的,它们各自的周期时间计算只与自己的cfs_rq有关,与组调度(其它的cfs_rq)是毫无关系的。这样,可以想象,底层tg可能会由于上级tg的slice用完而reschedule,但其实自己的slice还有剩余,这是一种正常现象。因为底层的调度实体,周期以及各个slice的计算是独立与其它层的,但是调度控制还是受制于其它的(这里的“其它”是指自底向上,直至根节点的整个路径层次)。 5 与负载均衡的关系 在迁移进程时,也会针对迁移进程做上述扩散处理,相当于进程在tg内的迁移而已。只是,进程的选择需要大作文章,涉及调度域,这是另一个层次结构,与组调度的层次结构都无关。 6 总结 组调度将调度的粒度灵活化,并行保证了tg和task的粒度,这是由tg shares在cpu间划分贯穿树层次结构来实现的,同时伴以SMP负载均衡的辅佐。 新浪微博(@NP等不等于P) 计算机学习微信公众号(jsj_xx) 原创技术文章,感悟计算机,透彻理解计算机! |
|