Linux下进程调度与优先级的深入分析

时间:2022-11-17 22:07:46

1)初识调度


为配合系统对进程的调度,采用两种方式进行处理
1.1)协同多任务处理

当进程因为事件的等待,而自动放弃CPU资源,而使其它进程得以使用CPU,这时称之为协同多任务处理.
其实如果进程都采用协同多任务处理时,系统将会轻松的调度进程,以分配资源

1.2)抢先式多任务处理
当进程不进行I/O,比如计算型运算应用时,一直占用大量的CPU时间,这时系统将会利用中断,使原占用CPU的进程放弃CPU.
这时称之为抢先式多任务处理

1.3)总结
1.3.1)UNIX/LINUX采用协同多任务处理与抢先式多任务处理相结合的方式处理进程.
1.3.2)当进程都采用协同多任务处理,系统就可能永远不会用到抢先式.
1.3.3)调度程序本身也是一个进程,PID是0,是INIT进行的父进程

 

 


2)阻塞,抢先占用和放弃


2.1)抢先占用的触发:

2.1.1)当一个进程因为它的时间片用完而被内核停止执行时,就说这个进程被抢占.
2.1.2)当一个享有更高优先公的进程在就绪队列,内核同样可以抢占正在执行的进程,即使它的时间片还没用完.


2.2)放弃的触发:
2.2.1)用户进程可以通过sched_yield系统调用来放弃使用CPU.
2.2.2)用户进程通过其它系统调用来放弃CPU.
例如:一个进程正在调用read/write时,它很可能不得不等待相应的设备的响应,这时它应用使自己睡眠(sleep),放弃CPU,直到设备就绪为止.


2.3)阻塞的触发:
2.3.1)当进程在内核模式下等待一个事件时,就是阻塞,直到所等侍的事件来唤醒它为止.

2.4)总结:
2.4.1)阻塞与放弃的区别
一个阻塞的进程即不会占用CPU,也不会被调度程序调度.而放弃的进程则会被调度程序调度.
2.4.2)linux2.6内核是一个可抢先占有的内核,在不可抢先有的内核则高优先级的进程不能抢占低优先级的进程.

 

 

3)优先级

3.1)动态优先级
因为高优先级的进程总比低优先级的进程先被调度,为防止有多个高优先级且一直占用CPU资源,导致其它进程不能占用CPU,所以引用动态优先级概念.

3.2)交互式的进程
如果某个进程被认为是交互式的,那么将被赋予较高的优先权,例如:键盘输入.交互式的进程从来不被抢占,并且占用CPU很少,它们常常自动放弃CPU.

3.3)有效优先级
进程的有效优先级就是静态优先级与额外值的和.

静态优先级是在系统创建时就已经分配给进程了,整个运行周期里不变.
额外值是一个可正可负的值,也可以把额外值理解为动态优先级,因为系统通过改变这个额外值(动态优先级)来改变进程的有效优先级.
额外值是由内核来管理

3.4)用一个例子来说明系统是如何给进程分配动态优先级的

新建nicegy脚本,这个脚本用sleep使其部分时间在睡眠状态.
#!/bin/sh
while true; do
 sleep .1
done

新建cruncher脚本,这个脚本在循环中使用true,一直占用CPU.
#!/bin/sh
while true; do
 true
done


新建脚本runex,这个脚本使行cruncher和niceguy两个脚本,监控这两个脚本的动态优先级.
#!/bin/sh

./cruncher &
./niceguy &


trap 'echo stoping; kill %1 %2; break' SIGINT

while true; do
        ps -C niceguy -C cruncher -o etime,pid,pri,cmd
        sleep .5
done

运行runex

./runex
    ELAPSED   PID PRI CMD
      00:00  2343  20 /bin/sh ./cruncher
      00:00  2344  21 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:01  2343  20 /bin/sh ./cruncher
      00:01  2344  22 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:01  2343  19 /bin/sh ./cruncher
      00:01  2344  22 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:02  2343  18 /bin/sh ./cruncher
      00:02  2344  23 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:02  2343  15 /bin/sh ./cruncher
      00:02  2344  23 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:03  2343  14 /bin/sh ./cruncher
      00:03  2344  23 /bin/sh ./niceguy
    ELAPSED   PID PRI CMD
      00:04  2343  14 /bin/sh ./cruncher
      00:04  2344  23 /bin/sh ./niceguy
     
结论:
1)这两个进程会随时时间变化而变化
进程cruncher会一直运行不休眠(sleep),因此调度程序会给它一个负的额外值以降低它的有效优先级.
而进程niceguy大部从那时间则处于休息状态,因此调度程序会给它一个正的额外值以提高它的有效优先级.
2)而这两个进程在此之后就处于相对稳定的状态.
3)PRI是有效优先级,系统不能改变进程的静态优先级,但可以改变它的额外值以达到降低有效优先级的目的.
例如:静态优先级是20,额外值是-6,则有效优先级就是PRI=20+(-6)=14,注意这里没有涉及nice值,我们下面再来谈nice值对优先级的影响.

 

 


4)nice值与有效优先级

4.1)nice值的由来
内核允许用户通过使用一个名为nice的数值来影响调度程序关于优先级的调度
也就是说nice值是用户来触发,而我们上面说的额外值是内核来触发的.

4.2)nice值对有效优先级的影响

nice值越高,有效优先级就越低.
nice值越低,有效优先级就越高.
有效优先级=静态优先值+额外值-nice值
例如:
静态优先值为20
额外值为-6
nice值为-10
那最后有效优先级就是:
PRI=20+(-6)-(-10)=24
而如果nice值为10
PRI=20+(-6)-10=4


4.3)nice与有效优先级的范围

nice的范围是-20到19
有效优先级的范围是0-39


4.4)例子:nice是如何影响有效优先级的

cruncher进程的有效优先级是14,nice值是0

ps -C cruncher -o etime,pid,pri,ni,cmd
    ELAPSED   PID PRI  NI CMD
      00:17  6034  14   0 /bin/sh ./cruncher

更改nice值为-20
renice -20 -p 6034
6034: old priority 0, new priority -20

当nice的值为-20,PRI的值变成34
PRI = 静态优先级(20) + 附加值(-6) - nice(-20) = 34
ps -C cruncher -o etime,pid,pri,ni,cmd
    ELAPSED   PID PRI  NI CMD
      00:39  6034  34 -20 /bin/sh ./cruncher

更改nice的值为19
renice 19 -p 6034
6034: old priority -20, new priority 19

当nice的值为19,PRI的值为0
PRI = 静态优先级(20) + 附加值(-6) - nice(19) = 0 因为最低0,即使是-5最后也0

ps -C cruncher -o etime,pid,pri,ni,cmd
    ELAPSED   PID PRI  NI CMD
      01:03  6034   0  19 /bin/sh ./cruncher

注意:PRI为34时,系统的反应会变慢.

 

 

5)实时优先级

5.1)实时优先级概述:
linux提供了一个实时调度策略,提供了100个额外优先级别,以保证系统能及时响应进程.

5.2)实时优先级和普通优先级的区别:
实时优先级比普通优先级高,普通优先级是0-39.而实时优先级是(41-139),优先级一般不用40.

实时优先级在整个进程的生命周期里是不变的,所以实时进程没有nice值,也没有额外值.它的有效优先级就是静态优先级.

5.3)POSIX标准为实时进程指定了两种策略:先进先出(FIFO),时间片法(Round_Robin,简称RR)

5.3.1)先进先出(FIFO)调度
如果有两个同优先级的进程位于就绪队列,通常情况下,排在前面的先被执行

FIFO的策略要求进程不能抢占CPU,除非另一个实时进程有更高优先级

5.3.2)FIFO调度的例子
这个程序将永远不会退出.系统将会死机.

新建chewer脚本
#!/bin/sh
(sleep 50;kill -ALRM $$) &
while true;do
true;
done

程序为什么不在50秒之后退出呢,原因是一个fifo的调度,是不会运行(sleep 5;kill -ALRM $$),而永远进入了一个死循环.
在2.6.18的内核中,这个会造成死机,而新的2.6.32内核则不会,我们依然有机会终止它.
我们在2.6.32的内核中看下它的优先级

指定调度策略为FIFO,优先级为50
chrt -f 50 ./chewer &
[2] 2414

查看优先级,CLS显示了调度策略,chewer的调度策略是FF,FF表示FIFO,PTRPIO是实时优先级,这里chewer是50,
PRI是有效优先级,在实时系统里也叫绝对优先级,有效优先级是实时优先级与40的和,所以这里是90.

ps -eo pid,ppid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,command
  PID  PPID   TID CLS RTPRIO  NI PRI PSR %CPU STAT WCHAN          COMMAND
    1     0     1 TS       -   0  19   0  0.0 Ss   poll_schedule_ init [2] 
    2     0     2 TS       -   0  19   0  0.0 S    kthreadd       [kthreadd]
    3     2     3 FF      99   - 139   0  0.0 S    migration_thre [migration/0]
    4     2     4 TS       -   0  19   0  0.0 S    ksoftirqd      [ksoftirqd/0]
    5     2     5 TS       -   0  19   0  0.0 S    worker_thread  [events/0]
    6     2     6 TS       -   0  19   0  0.0 S    worker_thread  [khelper]
   11     2    11 TS       -   0  19   0  0.0 S    async_manager_ [async/mgr]
   58     2    58 TS       -   0  19   0  0.0 S    bdi_sync_super [sync_supers]
   60     2    60 TS       -   0  19   0  0.0 S    bdi_forker_tas [bdi-default]