linux内核调度 - 图文(6)

2019-04-23 20:18

进程增加的数值是不同的。比如nice值为0的进程运行了10ms,其虚拟运行时间增加了1vms(vms为1虚拟毫秒,为了描述方便而定义);nice为19的进程运行10ms,其虚拟运行时间增加了1000vms。进程在其生命周期中,其虚拟运行时间一直都是在增加的。内核把虚拟运行时间看做实际运行时间,为了公平起见要选择运行时间短的进程进行运行。所以内核在调度中总是选择虚拟运行时间小的进程。对内核来说,这样就很公平了,O(∩_∩)O

同样运行10ms,如何确定一个进程该增加多少vms?增加的虚拟运行时间和进程的优先级nice数值有关,每个nice数值对应一个权重数值,见下图。

每个进程虚拟运行时间增加的时间量和 (NICE_0_LOAD/nice_n_weight) 成正比。其中NICE_0_LOAD 为1024,即nice数值为0的权重,nice_n_weight即为nice数值为n的进程的权重,如nice为-20(优先级最高的普通进程)的权重为88761。从这种算法也可以看出,优先级高的进程的虚拟运行时间增加的慢,其实际运行时间累计数值也就长。同样,这种算法能保证优先级低的进程也有运行机会,只是实际运行的时间比较短。

内核要把所有running状态的进程放到一起,在需要调度时从中取出一个虚拟运行时间短的进程。因为发生调度的频率非常高,查找合适进程的算法就变的很重要了。在CFS调度中,内核采用了以进程虚拟运行时间为key值的红黑树数据结构挂接各个running的进程。有关红黑树请参考《linux内核之红黑树》。 先要引入一个重要概念,调度实体(sched entiy):就是调度的对象。每个进程的task_struct包含了调度实体成员变量se。为何要引入调度实体,而不是直接使用进程的task_struct?是因为在CFS中支持CFS组调度,一个组中可能包含1个或多个进程。不能通过组中任何进程的task_struct来调度组,所以引入了调度实体的概念。为了统一起见,进程组和进程都使用调度实体保存调度相关的信息。后面会介绍CFS组调度。

在多核系统中,每个CPU(此处指一个核心)对应一个全局变量

per_cpu_runqueues,其数据结构为struct rq,该变量为调度的最顶层的数据结构。在该数据结构中包含了cfs,其数据结构为struct cfs_rq。cfs 就是在该cpu上CFS调度的顶级结构,或者说是CFS调度的入口点。其实rq中还包括了rt成员变量,rt是实时进程调度的顶级结构。

struct cfs_rq { 为了说明方便,只保留部分成员变量 struct rb_root tasks_timeline; struct rb_node *rb_leftmost;

struct sched_entity *curr, *next, *last, *skip; }

其中成员变量tasks_timeline指向了红黑树的根,所有的进程都挂到这棵红黑树上(有些是间接挂接的)。如下图中的单个进程,进程数据结构task_struct中包含了成员变量se,即调度实体。调度实体se中包含了run_node节点,se通过该节点挂接到红黑树上。在选择需要调度的进程时,内核将搜索这个红黑树,找到虚拟运行时间小的进程,并把该进程从树上摘下。同时会把切换出(因运行时间长而被剥夺运行的进程插入红黑树)。由于红黑树的特性,其插入、摘除和超找的效率都很高,从而保证了CFS调度的效率。 因下图不清楚,直接把原始的word文档传上了:组调度.doc

linux内核之CFS调度和

CFS组调度

为何要引入CFS组调度呢?假设用户A和B共用一台机器。我们可能希望A和B能公平的分享CPU资源,但是如果用户A使用创建了8个进程、而用户B只创建1个进程,根据CFS调度是基于进程的(假设用户进程的优先级相同),A用户占用的CPU的时间将是B用户的8倍。

如何保证A、B用户公平分享CPU呢?组调度就能做到这一点。把属于用户A和B的进程各分为一组,调度程序将先从两个组中选择一个组,再从选中的组中选择一个进程来执行。如果两个组被选中的机率相当,那么用户A和B将各占有约50%的CPU。

相关数据结构

在linux内核中,使用task_group结构来管理组调度的组。所有存在的task_group组成一个树型结构。

一个task_group可以包含具有任意调度类别的进程(具体来说是实时进程和普通进程两种类别),task_group包含实时进程对应的调度实体和调度队列,以及普通进程对应的调度实体和调度队列。见下面的结构定义 struct task_group{ 删除无关的成员变量 #ifdef CONFIG_FAIR_GROUP_SCHED

struct sched_entity **se; 普通进程调度实体,每个cpu上一个 struct cfs_rq **cfs_rq; 普通进程调度队列,每个cpu上一个 #endif

#ifdef CONFIG_RT_GROUP_SCHED

struct sched_rt_entity **rt_se; 实时进程调度实体,每个cpu上一个 struct rt_rq **rt_rq; 实时进程调度队列,每个cpu上一个 #endif }

下面只描述CFS组调度。在多核处理器平台上,内核在创建组的时候,内核会在每个cpu上给该组创建一个调度实体和一个调度队列,见上面图中红色方框外的部分。组在各个cpu上的调度实体se是单独被调度的。红色方框内是指在一个cpu上的调度数据结构。如果组在该cpu上有可以运行的进程,则组在该cpu上的调度实体se就会挂到该cpu上cfs_rq的红黑树上。组在该cpu上可以运行的进程则挂到了组调度实体se中my_q指向的红黑树上,参见上图中右下角单个进程。

CFS组的优先级。组在创建时其优先级是固定的,其nice值为0。组在某cpu上所能获得的运行时间和一个单独的nice为0的进程获得的运行时间相同。组的se在获得了一定运行时间后,按照CFS算法相同的方法把实际运行时间分配给其my_q上的所有进程。从而实现了A用户和B用户占用相同的CPU时间的目的。

疑问:有关进程权重表使用的疑问,在计算进程虚拟运行时间时,总是需要进行一次乘法和一次除法运算。大家都知道乘法和除法运算是比较消耗CPU周期的,为何不直接修改一下权重表prio_to_weight[]中的数值,使得其中的每一项数值都等于目前的数值除以15后取整(当然是人工计算出结果后替换到表中),使得优先级最小的进程的权重为1。在计算虚拟运行时间时可以直接乘以权重值,这样就可以减少一次除法了。这样是否更好?

另外,从代码中看到,nice为0的进程在计算虚拟运行时间时是直接增加了真实的时间差,而没有进行乘和除的过程,如下图,是否是因为设计初衷认为大多数的普通进程的nice值都是0的缘故?

linux内核的三种调度方法

1,SCHED_OTHER 分时调度策略,

2,SCHED_FIFO实时调度策略,先到先服务 3,SCHED_RR实时调度策略,时间片轮转

实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。

SHCED_RR和SCHED_FIFO的不同:

当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。

SCHED_FIFO一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。

如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。 相同点:

RR和FIFO都只用于实时任务。 创建时优先级大于0(1-99)。 按照可抢占优先级调度算法进行。 就绪态的实时任务立即抢占非实时任务。

所有任务都采用linux分时调度策略时。

1,创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。 2,将根据每个任务的nice值确定在cpu上的执行时间(counter)。 3,如果没有等待资源,则将该任务加入到就绪队列中。

4,调度程序遍历就绪队列中的任务,通过对每个任务动态优先级的计算(counter+20-nice)结果,选择计算结果最大的一个去运行,当这 个时间片用完后(counter减至0)或者主动放弃cpu时,该任务将被放在就绪队列末尾(时间片用完)或等待队列(因等待资源而放弃cpu)中。 5,此时调度程序重复上面计算过程,转到第4步。

6,当调度程序发现所有就绪任务计算所得的权值都为不大于0时,重复第2步。

所有任务都采用FIFO时,

1,创建进程时指定采用FIFO,并设置实时优先级rt_priority(1-99)。


linux内核调度 - 图文(6).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:Unit 2 Space Invaders课文翻译综合教程四

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: