`
russelltao
  • 浏览: 153006 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

linux内核调度算法(2)--CPU时间片如何分配

 
阅读更多

内核在微观上,把CPU的运行时间分成许多分,然后安排给各个进程轮流运行,造成宏观上所有的进程仿佛同时在执行。双核CPU,实际上最多只能有两个进程在同时运行,大家在top、vmstat命令里看到的正在运行的进程,并不是真的在占有着CPU哈。

所以,一些设计良好的高性能进程,比如nginx,都是实际上有几颗CPU,就配几个工作进程,道理就在这。比如你的服务器有8颗CPU,那么nginx worker应当只有8个,当你多于8个时,内核可能会放超过多个nginx worker进程到1个runqueue里,会发生什么呢?就是在这颗CPU上,会比较均匀的把时间分配给这几个nginx worker,每个worker进程运行完一个时间片后,内核需要做进程切换,把正在运行的进程上下文保存下来。假设内核分配的时间片是100ms,做进程切换的时间是5ms,那么进程性能下降还是很明显的,跟你配置的worker有关,越多下降得越厉害。

当然,这是跟nginx的设计有关的。nginx是事件驱动的全异步进程,本身设计上就几乎不存在阻塞和中断,nginx的设计者就希望每一个nginx worker可以独占CPU的几乎全部时间片,这点就是nginx worker数量配置的依据所在。


当然,实际的运行进程里,大部分并不是nginx这种希望独占CPU全部时间片的进程,许多进程,比如vi,它在很多时间是在等待用户输入,这时vi在等待IO中断,是不占用时间片的,内核面对多样化的进程,就需要技巧性的分配CPU时间片了。


内核分配时间片是有策略和倾向性的。换句话说,内核是偏心的,它喜欢的是IO消耗型进程,因为这类进程如果不能及时响应,用户就会很不爽,所以它总会下意识的多分配CPU运行时间给这类进程。而CPU消耗进程内核就不太关心了。这有道理吗?太有了,CPU消耗型慢一点用户感知不出来,电信号和生物信号运转速度差距巨大。虽然内核尽量多的分配时间片给IO消耗型进程,但IO消耗进程常常在睡觉,给它的时间片根本用不掉。很合理吧?


那么内核具体是怎么实现这种偏心呢?通过动态调整进程的优先级,以及分配不同长短的CPU时间处来实现。先说内核如何决定时间片的长度。

对每一个进程,有一个整型static_prio表示用户设置的静态优先级,内核里它与nice值是对应的。看看进程描述结构里的static_prio成员。

struct task_struct {
	int prio, static_prio;
......}

nice值是什么?其实就是优先级针对用户进程的另一种表示法,nice的取值范围是-20到+19,-20优先级最高,+19最低。上篇曾经说过,内核优先级共有140,而用户能够设置的NICE优先级如何与这140个优先级对应起来呢?看代码:

#define MAX_USER_RT_PRIO	100
#define MAX_RT_PRIO		MAX_USER_RT_PRIO
#define MAX_PRIO		(MAX_RT_PRIO + 40)

可以看到,MAX_PRIO就是140,也就是内核定义的最大优先级了。

#define USER_PRIO(p)		((p)-MAX_RT_PRIO)
#define MAX_USER_PRIO		(USER_PRIO(MAX_PRIO))

而MAX_USER_PRIO就是40,意指,普通进程指定的优先级别最多40,就像前面我们讲的那样-20到+19。


#define NICE_TO_PRIO(nice)	(MAX_RT_PRIO + (nice) + 20)

nice值是-20表示最高,对应着static_prio是多少呢?NICE_TO_PRIO(0)就是120,NICE_TO_PRIO(-20)就是100。


当该进程刚被其父进程fork出来时,是平分其父进程的剩余时间片的。这个时间片执行完后,就会根据它的初始优先级来重新分配时间片,优先级为+19时最低,只分配最小时间片5ms,优先级为0时是100ms,优先级是-20时是最大时间片800ms。我们看看内核是如何计算时间片长度的,大家先看下task_timeslice时间片计算函数:

#define SCALE_PRIO(x, prio) \
	max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE)

static unsigned int task_timeslice(task_t *p)
{
	if (p->static_prio < NICE_TO_PRIO(0))
		return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);
	else
		return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);
}

这里有一堆宏,我们把宏依次列出看看它们的值:
# define HZ		1000	
#define DEF_TIMESLICE		(100 * HZ / 1000)

所以,DEF_TIMESLICE是100。假设nice值是-20,那么static_prio就是100,那么SCALE_PRIO(100*4, 100)就等于800,意味着最高优先级-20情形下,可以分到时间片是800ms,如果nice值是+19,则只能分到最小时间片5ms,nice值是默认的0则能分到100ms。


貌似时间片只与nice值有关系。实际上,内核会对初始的nice值有一个-5到+5的动态调整。这个动态调整的依据是什么呢?很简单,如果CPU用得多的进程,就把nice值调高点,等价于优先级调低点。CPU用得少的进程,认为它是交互性为主的进程,则会把nice值调低点,也就是优先级调高点。这样的好处很明显,因为1、一个进程的初始优先值的设定未必是准确的,内核根据该进程的实时表现来调整它的执行情况。2、进程的表现不是始终如一的,比如一开始只是监听80端口,此时进程大部分时间在sleep,时间片用得少,于是nice值动态降低来提高优先级。这时有client接入80端口后,进程开始大量使用CPU,这以后nice值会动态增加来降低优先级。


思想明白了,代码实现上,优先级的动态补偿到底依据什么呢?effective_prio返回动态补偿后的优先级,注释非常详细,大家先看下。

/*
 * effective_prio - return the priority that is based on the static
 * priority but is modified by bonuses/penalties.
 *
 * We scale the actual sleep average [0 .... MAX_SLEEP_AVG]
 * into the -5 ... 0 ... +5 bonus/penalty range.
 *
 * We use 25% of the full 0...39 priority range so that:
 *
 * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs.
 * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks.
 *
 * Both properties are important to certain workloads.
 */
static int effective_prio(task_t *p)
{
	int bonus, prio;

	if (rt_task(p))
		return p->prio;

	bonus = CURRENT_BONUS(p) - MAX_BONUS / 2;

	prio = p->static_prio - bonus;
	if (prio < MAX_RT_PRIO)
		prio = MAX_RT_PRIO;
	if (prio > MAX_PRIO-1)
		prio = MAX_PRIO-1;
	return prio;
}

可以看到bonus会对初始优先级做补偿。怎么计算出这个BONUS的呢?

#define CURRENT_BONUS(p) \
	(NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / \
		MAX_SLEEP_AVG)

可以看到,进程描述符里还有个sleep_avg,动态补偿完全是根据它的值来运作的。sleep_avg就是关键了,它表示进程睡眠和运行的时间,当进程由休眠转到运行时,sleep_avg会加上这次休眠用的时间。在运行时,每运行一个时钟节拍sleep_avg就递减直到0为止。所以,sleep_avg越大,那么就会给到越大的动态优先级补偿,达到MAX_SLEEP_AVG时会有nice值-5的补偿。


内核就是这么偏爱交互型进程,从上面的优先级和时间片分配上都能看出来。实际上,内核还有方法对交互型进程搞优待。上篇说过,runqueue里的active和expired队列,一般的进程时间片用完后进expired队列,而对IO消耗的交互型进程来说,则会直接进入active队列中,保证高灵敏的响应,可见什么叫万千宠爱于一身了。




分享到:
评论

相关推荐

    LINUX内核调度分析.pdf

    4. 时间片分配:调度的基本单位是时间片,不同优先级分配的时间片长度是有差别的,一般来说,每NICE级之间大约有10%时间间隔变化。 5. 调度执行的时机:进程状态转换时刻、当前进程时机片用完、设备驱动程序中设 置...

    Linux内核进程调度算法的分析、研究与改进.pdf

    Linux 内核进程调度算法的分析、研究与改进 Linux 操作系统是目前世界上最流行的操作系统之一,主要...实验结果表明,改进后的 Linux 2.4 内核调度算法能够提高系统的实时性和吞吐量,满足实时操作系统的应用要求。

    Linux CFS调度算法分析.pdf

    Linux CFS调度算法分析 Linux操作系统的核心是进程调度。进程调度是操作系统最重要的程序之一,同时进程调度程序也是多任务操作系统执行频度最高的一部分,操作系统的整体性能也决定于进程调度程序的性能。本文剖析...

    Linux 2.6内核进程调度策略与算法分析.pdf

    Linux 2.6内核采用了新的调度器,该调度器基于O(1)算法,取消了全局同步和重算循环,每个CPU分配时间片。文中详细分析了新调度器的策略和算法,并对其进行了总结。 Linux 2.6内核进程调度策略是以优先级调度为基础...

    基于Linux内核的CFS调度算法研究.pdf

    "基于Linux内核的CFS调度算法研究" 本文主要介绍了基于Linux内核的CFS调度算法的研究,讨论了CFS调度算法的思想和核心,分析了Linux操作系统中进程调度算法的演变过程,包括O(n)算法、O(1)算法和CFS算法,並对比了...

    Linux 2.6内核进程调度分析.pdf

    该算法的设计主要解决了 Linux 2.4内核调度算法中的一些缺陷,如调度算法不适合大量进程数量、预定义的时间片太长、I/O密集型进程判定策略不佳等问题。 Linux 2.4内核调度算法存在一些缺陷,如调度算法不适合大量...

    Linux进程调度算法分析.pdf

    在Linux操作系统中,进程调度算法基于分时技术,每个可运行的进程分配一个时间片,当进程运行结束或时间片到期时,进程就发生切换。在这个过程中,内核会扫描可运行进程链表,计算进程的优先级,并选择“最适合”的...

    LINUX2.6内核进程调度策略分析.pdf

    在LINUX2.6内核中,进程时间片的计算是在各个进程耗尽时间片时单独进行的,解决了LINUX2.4内核中的0(n)级的问题。 LINUX2.6内核进程调度策略是一个高效的、实时的、公平的进程调度系统,它解决了LINUX2.4内核中的...

    LINUX进程调度算法的分析.pdf

    本文对LINUX操作系统中的普通进程调度算法进行了分析,讨论了以进程为CPU时间分配单位和以用户为CPU时间分配单位的两种算法,并对它们在不同环境下的进程调度效率和公平性影响进行了探讨。 进程调度是操作系统中的...

    Linux内核的进程调度.pdf

    Linux内核使用了多种进程调度算法,以满足不同的应用场景。常见的调度算法包括: 1. 时间片轮转算法(Round Robin,RR):每个进程都被分配了一个时间片,在这个时间片内,进程可以执行自己的任务。 2. 多级反馈...

    Linux内核中进程调度的分析.pdf

    在Linux内核中,进程调度算法有多种,如先进先出(FIFO)策略、时间片轮转(Round Robin)策略、优先级调度策略等。FIFO策略是最简单的策略,即按照进程的到达顺序来执行进程。Round Robin策略是将系统分配给每个...

    Linux2.6内核标准教程(共计8-- 第1个)

    4.5.3 时间片分配 181 4.5.4 进程调度时机 182 4.6 进程切换过程分析 183 4.6.1 选取合适进程 183 4.6.2 完成上下文切换 184 4.7 空闲进程的初始化 187 4.7.1 空闲进程的内核态栈 187 4.7.2 空闲进程...

    Linux进程调度分析.pdf

    Linux 进程调度的原理是基于进程的运行需要系统上的各种资源,如内存、CPU、各种专用模块等,因此 Linux 进程的调度实际上就是合理地把这些资源分配给各个进程,而如何分配则是取决于具体的调度算法,不同的应用...

    进程的优先级与调度策略—Linux

    概述1.1 进程优先级1.2 普通进程的调度1.2.1 静态优先级和基本时间片1.2.2 动态优先级和平均睡眠1.3 实时进程的调度1.4 内核空间优先级2.调度策略2.1 进程的抢占2.2 调度算法2.3 O(1)调度2.4 调度模型——机制与策略...

    Linux2.6内核标准教程(共计8--第6个)

    4.5.3 时间片分配 181 4.5.4 进程调度时机 182 4.6 进程切换过程分析 183 4.6.1 选取合适进程 183 4.6.2 完成上下文切换 184 4.7 空闲进程的初始化 187 4.7.1 空闲进程的内核态栈 187 4.7.2 空闲进程...

    Linux2.6内核标准教程(共计8--第8个)

    4.5.3 时间片分配 181 4.5.4 进程调度时机 182 4.6 进程切换过程分析 183 4.6.1 选取合适进程 183 4.6.2 完成上下文切换 184 4.7 空闲进程的初始化 187 4.7.1 空闲进程的内核态栈 187 4.7.2 空闲进程...

    Linux2.6进程调度策略分析.pdf

    时间片是 Linux 调度的基础,多个进程以 “ 时间多路复用” 的形式运行,CPU 的时间被划分成小段,即所谓的时间片 ( slice) 。每个进程都会得到一个时间片,在具体某个时间,进程可以使用这个时间片来执行自己的任务...

    Linux2.6内核标准教程(共计8--第3个)

    4.5.3 时间片分配 181 4.5.4 进程调度时机 182 4.6 进程切换过程分析 183 4.6.1 选取合适进程 183 4.6.2 完成上下文切换 184 4.7 空闲进程的初始化 187 4.7.1 空闲进程的内核态栈 187 4.7.2 空闲进程...

    Linux2.6内核标准教程(共计8--第7个)

    4.5.3 时间片分配 181 4.5.4 进程调度时机 182 4.6 进程切换过程分析 183 4.6.1 选取合适进程 183 4.6.2 完成上下文切换 184 4.7 空闲进程的初始化 187 4.7.1 空闲进程的内核态栈 187 4.7.2 空闲进程...

    基于CFS组调度的CPU资源动态分配方法.pdf

    CFS调度算法的主要思想是基于公平性原则,根据进程的优先级和时间片长度来分配CPU资源。然而,在实际应用中,CFS调度算法存在一些缺陷,例如不能很好地处理组间的公平性问题。 为了解决这个问题,本文提出了基于CFS...

Global site tag (gtag.js) - Google Analytics