Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1326107
  • 博文数量: 175
  • 博客积分: 2743
  • 博客等级: 少校
  • 技术积分: 4024
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-30 01:41
文章分类

全部博文(175)

文章存档

2015年(1)

2013年(53)

2012年(71)

2011年(50)

分类: LINUX

2013-04-18 10:45:52

对于自己来说关键点:

1、网页:http://blog.csdn.net/rein07/article/details/6534801

Linux调度时机主要有:
1、进程状态转换的时刻:进程终止、进程睡眠;
2、当前进程的时间片用完时(current->counter=0);
3、设备驱动程序主动调用schedule;
4、进程从中断、异常及系统调用返回到用户态时;
时机1,进程要调用sleep()或exit()等函数进行状态转换,这些函数会主动调用调度程序进行进程调度;
时机2,由于进程的时间片是由时钟中断来更新的,因此,这种情况和时机4是一样的。
时机3,当设备驱动程序执行长而重复的任务时,直接调用调度程序。在每次反复循环中,驱动程序都检查need_resched的值,如果必要,则调用调度程序schedule()主动放弃CPU。
时 机4,如前所述,不管是从中断、异常还是系统调用返回,最终都调用ret_from_sys_call(),由这个函数进行调度标志的检测,如果必要,则 调用调度程序。那么,为什么从系统调用返回时要调用调度程序呢?这当然是从效率考虑。从系统调用返回意味着要离开内核态而返回到用户态,而状态的转换要花 费一定的时间,因此,在返回到用户态前,系统把在内核态该处理的事全部做完


2、网页http://russelltao.iteye.com/blog/1405359

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

 

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


Cpp代码
  1. #define SCALE_PRIO(x, prio) \  
  2.     max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE)  
  3. static unsigned int task_timeslice(task_t *p)  
  4. {  
  5.     if (p->static_prio < NICE_TO_PRIO(0))  
  6.         return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);  
  7.     else  
  8.         return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);  

这里有一堆宏,我们把宏依次列出看看它们的值:
Cpp代码
  1. # define HZ     1000      
  2. #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。


文章转自 http://russelltao.iteye.com/blog/1405359

内核在微观上,把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成员。

 

Cpp代码
  1. struct task_struct {  
  2.     int prio, static_prio;  
  3. ......}  

 

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

 

 

 

Cpp代码
  1. #define MAX_USER_RT_PRIO    100  
  2. #define MAX_RT_PRIO     MAX_USER_RT_PRIO  
  3. #define MAX_PRIO        (MAX_RT_PRIO + 40)  

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

 

 

Cpp代码
  1. #define USER_PRIO(p)        ((p)-MAX_RT_PRIO)  
  2. #define MAX_USER_PRIO       (USER_PRIO(MAX_PRIO))  

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

 

 

 

Cpp代码
  1. #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时间片计算函数:

 

Cpp代码
  1. #define SCALE_PRIO(x, prio) \  
  2.     max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE)  
  3. static unsigned int task_timeslice(task_t *p)  
  4. {  
  5.     if (p->static_prio < NICE_TO_PRIO(0))  
  6.         return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);  
  7.     else  
  8.         return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);  
  9. }  

这里有一堆宏,我们把宏依次列出看看它们的值:
Cpp代码
  1. # define HZ     1000      
  2. #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返回动态补偿后的优先级,注释非常详细,大家先看下。

 

Cpp代码
  1. /* 
  2.  * effective_prio - return the priority that is based on the static 
  3.  * priority but is modified by bonuses/penalties. 
  4.  * 
  5.  * We scale the actual sleep average [0 .... MAX_SLEEP_AVG] 
  6.  * into the -5 ... 0 ... +5 bonus/penalty range. 
  7.  * 
  8.  * We use 25% of the full 0...39 priority range so that: 
  9.  * 
  10.  * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs. 
  11.  * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks. 
  12.  * 
  13.  * Both properties are important to certain workloads. 
  14.  */  
  15. static int effective_prio(task_t *p)  
  16. {  
  17.     int bonus, prio;  
  18.     if (rt_task(p))  
  19.         return p->prio;  
  20.     bonus = CURRENT_BONUS(p) - MAX_BONUS / 2;  
  21.     prio = p->static_prio - bonus;  
  22.     if (prio < MAX_RT_PRIO)  
  23.         prio = MAX_RT_PRIO;  
  24.     if (prio > MAX_PRIO-1)  
  25.         prio = MAX_PRIO-1;  
  26.     return prio;  
  27. }  

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

 

 

Cpp代码
  1. #define CURRENT_BONUS(p) \  
  2.     (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / \  
  3.         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队列中,保证高灵敏的响应,可见什么叫万千宠爱于一身了。


阅读(2959) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~