CFS-完全公平调度器

CFS实现的公平的基本原理是这样的:指定一个周期,根据进程的数量,大家”平分“这个调度周期内的CPU使用权,调度器保证在一个周期内所有进程都能被执行到。CFS和之前O(n)调度器不同,优先级高的进程能获得更多运行时间,但不代表优先级高的进程一定就先运行:调度器使用vruntime来统计进程运行的累计时间,理想状态下,所有进程的vruntime是相等时代表当前CPU的时间分配是完全公平的。但事实...
摘要由CSDN通过智能技术生成

CFS实现的公平的基本原理是这样的:指定一个周期,根据进程的数量,大家”平分“这个调度周期内的CPU使用权,调度器保证在一个周期内所有进程都能被执行到。CFS和之前O(n)调度器不同,优先级高的进程能获得更多运行时间,但不代表优先级高的进程一定就先运行:

调度器使用vruntime来统计进程运行的累计时间,理想状态下,所有进程的vruntime是相等时代表当前CPU的时间分配是完全公平的。但事实上,即使是多核的系统一般进程数也是大于核心数的,所以一旦有进程占用CPU运行势必会造成不公平,完全公平调度器通过让当前遭受不公最严重(vruntime最小)的进程优先运行来缓解不公平的情况。当然,vruntime所指的运行时间并未非和以往一样每个或每几个cpu tick周期增加1,需要经过优先级加权换算,优先级高的进程可能运行10个tick之后vruntime才加1,反之优先级低的进程可能运行1个tick之后vruntime就被加了10。

附一张图:

1.调度周期如何规定?

CFS引入了一个动态变化的调度周期:period。看两个CFS开放给用户的参数:

{//字面意思调度最小粒度,即进程每次被调度到最少要占用多长时间CPU
	.procname	= "sched_min_granularity_ns",
	.data		= &sysctl_sched_min_granularity,
},
{//字面意思调度延迟,即每个进程最长不等待超过调度延迟会被再次调度
	.procname	= "sched_latency_ns",
	.data		= &sysctl_sched_latency,
},

 

设想在最坏的情况下:只有一个CPU,等待运行的所有进程优先级相同所分得时间片相同,当一个进程运行过之后就要等待所有其他进程都运行到之后才能再次被调度。所以用户设置的这两个参数其实只有在rq上面进程数nr_running大于sysctl_sched_latency/sysctl_sched_min_granularity时才能被满足。

//每次更新最小调度粒度和调度延迟两个参数,都会更新sched_nr_latency
sched_nr_latency = DIV_ROUND_UP(sysctl_sched_latency, sysctl_sched_min_granularity);
//获取动态调度周期period需要根据sched_nr_latency计算
static u64 __sched_period(unsigned long nr_running)
{
	if (unlikely(nr_running > sched_nr_latency))
		return nr_running * sysctl_sched_min_granularity;
	else
		return sysctl_sched_latency;
}

 

从上面代码可以看出来,当进程太多时CFS只能先不管调度延迟,只保证最小调度粒度。为什么不保证用户设置调度延迟而保证最小调度粒度?因为最小调度粒度不保证的话,频繁抢占进程只会让更多时间浪费在context switch上,进一步恶化CPU资源紧促的情况。

 

2.CFS是如何分配时间片的?

CFS引入了vruntime虚拟运行时间的概念,为了让所有进程vruntime趋于相等,每次pick_next_task挑选下个要被运行的进程总会挑vruntime最小的进程出来运行。

但是vruntime和wall-time是不相等的,还要通过优先级加权,也就是说同样运行了10ms,高优先级进程vruntime+1低优先级进程可能要+10。

看看se中和cfs_rq中相关的成员:

tast_struct.se:
struct sched_entity {
	struct load_weight		load;
	unsigned int			on_rq;
};
cfs_rq:
struct cfs_rq {
	struct load_weight load;
	unsigned int nr_running, h_nr_running;
	u64 min_vruntime;
}
load_weight:
struct load_weight {
	unsigned long			weight;
	u32				inv_weight;
};

 

主要就是load_weight这个结构,load_weight字面意思就是权重,调度实体中的load_weight代表的该进程的权重,工作列队里的load_weight代表了整个列队的总权重。

看看CFS如何分配时间片:

static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	//获得当前的调度周期
	u64 slice = __sc
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值