linux进程调度之 FIFO 和 RR 调度策略

时间:2021-12-30 21:16:41

http://blog.chinaunix.net/uid-24774106-id-3372932.html

http://blog.chinaunix.net/uid-24774106-id-3379478.html

 

chrt -p <pid>

SCHED_OTHERSCHED_FIFOSCHED_RR 

 

 最近几天结合源码看了很多linux进程调度的文章,虽然掌握了个大概,但是越看,细节越多,写这篇文章的信心也就越不足,曾有系列文章叫鼠眼看linux进程调度,很符合我现在的心境,就像盲人摸象,学到一些东西,很惊喜,但是总有一种力不从心的惶恐。但是好久没写博文了,还是写一篇。写的不对的地方,请大家批评指正。


    CPU是一种宝贵的资源,Linux是多任务的OS,这种多任务要求,对于每个任务或者说进程来说,就好像它独占了CPU。想想如果只有一个CPU,但是有80个处于TASK_RUNNING状态的进程,在这八十个进程选择进程,切换进程是多么困难。如果你理解不了这种难度,你可以想想如果你同时有80个女朋友,性格迥异,爱好不同,要做到每个女朋友都认为她自己是你唯一的女友是多么的困难。

   刘明在《Linux 调度器BFS简介》中有个很有意思的比喻,linux 内核调度器就像处境尴尬的主妇,满足孩子对晚餐的要求便有可能伤害到老人的食欲,做出一桌让男女老少都满意的饭菜实在是太难了。Linux是一个通用的操作系统,无法预测运行其上的进程有什么样的特点,就像一家人的口味各不相同,孩子喜欢吃甜,爸爸喜欢吃咸,老人喜欢口味清淡,所以调度器也很为难。

    一般来说根据进程的特点,可以将进程分成几类
1 交互式进程
   这个比较简单了,想想你你使用vi编辑文本,大量的人机交互,进程不断的进入睡眠状态等待你的输入,CPU占用不高但是要求响应迅速,比如你敲了键盘,过了10秒钟才在编辑器上显示出来,这用户体验是多么的糟糕。

2 批处理进程
    最典型的就是编译工程这种进程了,如果我们编译一个大型的工程,我们敲了个make,也许1个小时才能编译成功,但是没有人会两个眼睛盯着屏幕看编译的过程,我们更关心的是编译的结果。对于这种进程就是属于姥姥不疼舅舅不爱的进程,这要他在跑就行了,不必给他太多的关注和资源。

3 实时进程
    实时进程原本的含义是给定的工作要在指定的时间内完成,不要求你多快,但是一定要在指定的时间前完成指定的任务,多用在火箭 导弹类似的精密系统中。这种实时叫做硬实时,对于Linux 这种通用的OS来讲,完全做到硬实时有点强人所难了,中断,磁盘寻道,总线征用 锁,等很多的机制带来了太多的不确定性,很难做到硬实时。

    linux所说的实时进程是软实时,就是尽量的实时,如果做不到,也不会出现毁灭性的后果。比如视频播放器,解码速度有点慢,顶多是视频播放有点卡,用户体验不好,但是不会机毁人亡。

    不同的进程,就用不同的调度策略伺候之,对于实时进程,我们就采用实时调度策略:FIFO or Round Robin(时间片轮转)。对于批处理进程和交互进程,我们采用CFS调度器。在之前的版本,Linux采用O(1)调度器的时候,有复杂的方法识别交互性进程,奖励交互性进程,算法比较复杂,现在采用CFS调度器后,核心思想比较简单“完全公平”,降低了代码复杂度,很好地满足了各种需求。

    先说说实时调度,实时调度有两个维度1 优先级2 调度策略。linux用户程序可以通过调用sched_setscheduler系统调用来设置进程的优先级和调度策略。

    实时进程共有0~99共100种不同的优先级,0优先级最高,99优先级最低,对应100个队列。对于实时进程来说,高优先级实时进程存在,低优先级的实时进程就捞不着CPU资源,普通进程更捞不着CPU资源。
 
    接下来就是调度策略,一种是FIFO,先进先出,如果最高优先级的进程是FIFO类型,那么他就一直执行,一条路跑到黑,直到进程退出,或者它调用sched_yield主动让出资源,或者突然出现更高优先级的进程横空出世,抢占了它的CPU。另外一种是时间片轮转,假如说最高优先级实时进程队列存在多个进程,当前进程耗尽自己时间片后自动排到队尾,选择同一个队列的队列头部的进程去执行。这些事情是在task_tick_rt函数做的。

    
    前面提到实时进程存在,普通进程完全捞不到CPU资源也不完全对,后来LINUX增加组调度策略后,有/proc/sys/kernel/sched_rt_period_us和/proc/sys/kernel/sched_rt_runtime_us两个参数,这两个参数表示的含义是:在sched_rt_peroid_us的周期内,所有的实时进程运行时间之和不得超过sched_rt_runtime_us。 默认值为1秒和0.95秒换句话说,在1秒的周期以内,所有实时进程运行时间之和不得超过0.95秒,剩下的0.5秒钟留给普通进程。

    对于普通进程的CFS调度器,内容相对比较独立,如果有时间,后面会有专门的博文总结。

2 SMP时代的进程调度

    更要命的是现在进入了多核的时代,纵然不谈服务器的高配置,我的笔记本配置不高,也已经是四核,可见多核计算机已经是满大街了。对于负载的SMP,就引入了新的问题。单核的时候,只有一个CPU,不需要考虑选择几个运行队列,就是一个run queue。但是多核的时候,是建立一个run queue,所有的CPU去run queue上选择摘取可执行的进程,还是每个CPU一个run queue?其实都是可以的。

    1 per cpu run queue
     目前linux 内核采用的是per cpu run queue,每个CPU去自己的run queue 去选择可执行的进程,这样减少  了竞争。其实除了这个好处,还有另外一个甚至更重要的好处,缓存重利用。进程落在这个CPU的运行队列上,经过多次调度后(暂不考虑多CPU的load balance),仍然是同一颗CPU运行这个进程,也许上次运行时的变量 内存还在cache中,如果只有一个运行队列,多个CPU的话,上次执行所在的CPU和这次执行所在的CPU很可能不是同一颗CPU,那么上次执行调入的缓存就完全用不上了。

    2 single run  queue
    所有的CPU使用同一个队列,这也是可以的,有些人觉着太土,因为如果一个CPU 访问run queue的时候,其他的CPU 就不能访问run queue了,一把大锁降低了性能,尤其是当CPU特别多的时候,想想如果你有1024个CPU,然而只有一个运行队列,run queue这个瓶颈就是显而易见的了。

    介绍到这里,很多筒子可能就会说了,那还有啥好纠结的,肯定是per cpu run  queue 更优越。但是实际情况要比理论复杂的多。BFS(Brain Fuck Scheduler ,又称脑残调度器)就是一种单一队列的调度器,这种调度器对于桌面应用来说,效果非常的好。感兴趣的筒子可以阅读《Linux调度器BFS简介》《BFS,Linux桌面的极速未来》

     per cpu run queue也不是完全没有缺点。既然是多个队列,就会有可能不均衡,比如一个CPU忙的要死,另一个CPU无事可做,这种情景本身也是对CPU资源的浪费。内核为了解决这个问题就必须要发起CPU间的负载均衡load balance,两个CPU之间负载均衡,要获取两个run queue的锁,会伤害并发,除此以外,负载均衡本身也消耗CPU资源。

    我们按照我们的硬件常识,如果我有两颗物理CPU,每颗物理CPU有两个核,那么我的CPU就是4核的,每个核又可以通过SMT(Simultaneous Multi-Threading)类似的技术实现多个硬件线程或者叫做Virtual CPU,比如Intel的超线程技术,如果每个核可以实现2个Virtual CPU,那么我的硬件就有8个Virtual CPU。这些Virtual CPU 之间的亲缘关系是不同的。

    为了应对多核多CPU,Linux引进了调度域的概念,其实就是根据的亲缘关系的远近,划分不同的范围。根据亲缘关系的远近从近到远分成四个层次:
1 超线程
同一个核利用超线程技术演化出来的Virtual CPU。我们知道CPU的速度要比内存访问的速度快,如果cache没有命中,CPU在等待内存的时间里面无事可做,这是一种浪费,就切换到其他线程。这样多个线程分时复用一个核。说实话,这个超线程技术我其实并不是特别理解,改天研究下Intel手册看个究竟。

2 同一个物理CPU的不同Core

3 同一个NUMA结点上的不同物理CPU

4 不同NUMA节点上的物理CPU。

linux进程调度之 FIFO 和 RR 调度策略

    NUMA(非一致性内存系统),CPU和RAM是以结点为单位分组的,同一个CPU访问本结点内的本地RAM代价要比访问其他节点的RAM代价低。CPU亲缘关系越近,他们之间进程迁移的代价越低。比如说,将进程在同一个物理CPU下的两个Core之间迁移,代价要低于不同物理CPU之间的迁移,原因就是一部分cache可以继续使用。

    load balance,就是将进程从一个CPU(广义的CPU)迁移到另一个CPU,如果将进程从一个核的一个超线程域搬到另一个超线程域,没啥关系,因为代价不高,就好像让我从办公楼4楼搬到办公楼3楼,代价很低,但是CPU亲缘关系越远,代价越高,就好像上午还在南京上班,下午让我搬到乌鲁木齐上班,所以这种搬家尽量要少。事实上linux 内核也是这么做的。

    OK,继续我们的调度域:假如我们有两个物理CPU,每个物理CPU有两个Core,每个Core有通过超线程演化出两个Virtual CPU,那么我们看下下面这个图(这个图是从Linux内核SMP负载均衡浅析中拷贝的)
linux进程调度之 FIFO 和 RR 调度策略

     对于每个virtual CPU,属于一组调度域sched_domain,就像我是位于雨花区的,同时我也是位于南京,我还是江苏的,我还是位于中国的,我隶属的不同区域反应的不过是不同层次看我所处的位置。每个调度域分成多个组,就好像中国分成30多个省级行政单位,每个省级行政单位又分成不同的市,每个市又分成不同的区。

    这还只是普通进程的SMP 调度,想想实时进程,我们每个CPU都有队列,也许CPU-a的最高优先级的实时进程尚不如 CPU-b第二高优先级的实时进程,这时候,需要将CPU-b中的实时进程拽到CPU-a上面执行,不然,会出现低优先级的实时进程先执行的异常情况,这个拽的过程是有pull_rt_task 实现的。

参考文献   
Linux内核SMP负载均衡浅析
2 Linux 进程调度浅析
3 鼠眼看Linux调度器
4 Linux调度器BFS简介
5 BFS,Linux桌面的极速未来

 

linux进程调度相关的内核代码看了两遍左右,也看了一些讲述linux进程调度的一些文章,总想写个系列文章,把进程调度全景剖析一遍,但是总是感觉力不逮己,自己都不敢下笔写文章了。算了,还是不难为自己了,就随便写写自己的心得好了。


    在用户空间,或者应用编程领域 ,Linux提供了一些API或者系统调用来影响Linux的内核调度器,或者是获取内核调度器的信息。比如可以获取或者设置进程的调度策略、优先级,获取CPU时间片大小的信息。

    这些接口一旦在应用程序中调用,就像给湖面扔进一颗石子,对内核带来了那些的影响,其实这是我内心很激动很想分享的东西,但是内容好没有组织好,所以本文的主题暂不深入涉及这些系统调用及对应的内核层的代码。

    严格地说,对于优先级对于实时进程和普通进程的意义是不一样的。
    1 在一定程度上,实时进程优先级高,实时进程存在,就没有普通进程占用CPU的机会,(但是前一篇博文也讲过了,实时组调度出现在内核以后,允许普通进程占用少量的CPU时间,取决于配置)。
    2 对于实时进程而言,高优先级的进程存在,低优先级的进程是轮不上的,没机会跑在CPU上,所谓实时进程的调度策略,指的是相同优先级之间的调度策略。如果是FIFO实时进程在占用CPU,除非出现以下事情,否则FIFO一条道跑到黑。
     a)FIFO进程良心发现,调用了系统调用sched_yield 自愿让出CPU
     b) 更高优先级的进程横空出世,抢占FIFO进程的CPU。有些人觉得很奇怪,怎么FIFO占着CPU,为啥还能有更高优先级的进程出现呢。别忘记,我们是多核多CPU ,如果其他CPU上出现了一个比FIFO优先级高的进程,可能会push到FIFO进程所在的CPU上。
     c)FIFO进程停止(TASK_STOPPED or TASK_TRACED状态)或者被杀死(EXIT_ZOMBIE or EXIT_DEAD状态)
    d) FIFO进程执行了阻塞调用并进入睡眠(TASK_INTERRUPTIBLE OR TASK_UNINTERRUPTIBLE)。
    
    如果是进程的调度策略是时间片轮转RR,那么,除了前面提到的abcd,RR实时进程耗尽自己的时间片后,自动退到对应优先级实时队列的队尾,重新调度。

    下面我们就是来探究FIFO策略和RR策略的特点。为了降低理解的难度,我将我们启动的实时进程绑定到同一个核上。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<sys/time.h>
  5. #include<sys/types.h>
  6. #include<sys/sysinfo.h>
  7. #include<time.h>

  8. #define __USE_GNU
  9. #include<sched.h>
  10. #include<ctype.h>
  11. #include<string.h>

  12. #define COUNT 300000
  13. #define MILLION 1000000L
  14. #define NANOSECOND 1000

  15. void test_func()
  16. {
  17.     int i = 0;
  18.     unsigned long long result = 0;;

  19.     for(i= 0; i<8000;i++)
  20.     {
  21.         result += 2;
  22.     }

  23. }
  24. int main(int argc,char* argv[])
  25. {
  26.     int i;
  27.     struct timespec sleeptm;
  28.     long interval;
  29.     struct timeval tend,tstart;
  30.     struct tm lcltime = {0};
  31.     struct sched_param param;
  32.     int ret = 0;

  33.     if(argc!= 3)
  34.     {
  35.         fprintf(stderr,"usage:./test sched_method sched_priority\n");
  36.         return -1;
  37.     }

  38.     cpu_set_t mask ;
  39.     CPU_ZERO(&mask);
  40.     CPU_SET(1,&mask);

  41.     if (sched_setaffinity(0, sizeof(mask),&mask) == -1)
  42.     {
  43.         printf("warning: could not set CPU affinity, continuing...\n");
  44.     }
  45.     int sched_method = atoi(argv[1]);
  46.     int sched_priority = atoi(argv[2]);

  47.     /*    if(sched_method> 2 || sched_method< 0)
  48.         {
  49.         fprintf(stderr,"sched_method scope [0,2]\n");
  50.         return -2;
  51.         }
  52.         if(sched_priority> 99 || sched_priority< 1)
  53.         {
  54.         fprintf(stderr,"sched_priority scope [1,99]\n");
  55.         return -3;
  56.         }

  57.         if(sched_method== 1 || sched_method == 2)*/
  58.     {
  59.         param.sched_priority = sched_priority;
  60.         ret = sched_setscheduler(getpid(),sched_method,&param);
  61.         if(ret)
  62.         {
  63.             fprintf(stderr,"set scheduler to %d %d failed %m\n");
  64.             return -4;
  65.         }
  66.     }

  67.     int scheduler = sched_getscheduler(getpid());

  68.     fprintf(stderr,"the scheduler of PID(%ld) is %d, priority (%d),BEGIN time is :%ld\n",
  69.             getpid(),scheduler,sched_priority,time(NULL));

  70.     sleep(2);
  71.     sleeptm.tv_sec = 0;
  72.     sleeptm.tv_nsec = NANOSECOND;

  73.     for(i= 0;i<COUNT;i++)
  74.     {
  75.         test_func();
  76.     }



  77.     interval = MILLION*(tend.tv_sec- tstart.tv_sec)
  78.                +(tend.tv_usec-tstart.tv_usec);

  79.     fprintf(stderr," PID = %d\t priority: %d\tEND TIME is %ld\n",getpid(),sched_priority,time(NULL));
  80.     return 0;
  81. }
    上面这个程序有几点需要说明的地方
    1 为了降低复杂度,绑定到了同一个核上,我做实验的机器是四核(通过cat /proc/cpuinfo可以看到)
    2  sleep(2),是给其他进程得到调度的机会,否则无法模拟出多个不同优先级的实时进程并行的场景。sleep过后,就没有阻塞性的系统调用了,高优先级的就会占据CPU(FIFO),同等优先级的进程轮转(RR) 

  1. struct sched_param {
  2. /* ... */
  3. int sched_priority;
  4. /* ... */
  5. };
int sched_setscheduler (pid_t pid,
                        int policy,
                        const struct sched_param *sp);


      sched_setscheduler函数的第二个参数调度方法 :

  1. #define SCHED_OTHER 0
  2. #define SCHED_FIFO 1
  3. #define SCHED_RR 2
  4. #ifdef __USE_GNU
  5. # define SCHED_BATCH 3
  6. #endif
    SCHED_OTHER表示普通进程,对于普通进程,第三个参数sp->sched_priority只能是0
   SCHED_FIFO 和SCHED_RR表示实时进程的调度策略,第三个参数的取值范围为[1,99]。
   如果sched_setscheduler 优先级设置的值和调度策略不符合的话,会返回失败的。

   内核中有这么一段注释:
  1. /*
  2.      * Valid priorities for SCHED_FIFO and SCHED_RR are
  3.      * 1..MAX_USER_RT_PRIO-1, valid priorityfor SCHED_NORMAL,
  4.      * SCHED_BATCH and SCHED_IDLE is 0.
  5.      */
    LINUX系统提供了其他的系统调用来获取不同策略优先级的取值范围

  1. #include <sched.h>
  2. int sched_get_priority_min (int policy);
  3. int sched_get_priority_max (int policy);

    另外需要指出的一点是,应用层和内核层的优先级含义是不同的:
   首先说实时进程:实时进程的优先级设置可以通过sched_setsheduler设置,也可以通过sched_setparam设置优先级的大小。
  1. int sched_setparam(pid_t pid,const struct sched_param *sp);
    在用户层或者应用层,1表示优先级最低,99表示优先级最高。但是在内核中,[0,99]表示的实时进程的优先级,0最高,99最低。[100,139]是普通进程折腾的范围。应用层比较天真率直,就看大小,数字大,则优先级高。ps查看进程的优先级也是如此。有意思的是,应用层实时进程最高优先级的99,在ps看进程优先级的时候,输出的是139.

    下面是ps -C test -o pid,pri,cmd,time,psr 的输出:
  1. PID   PRI  CMD             TIME     PSR
  2.  6303 139 ./test 1 99  00:00:04       1
     虽说本文主要讲的是实时进程,但是需要插句话。对于普通进程,是通过nice系统调用来调整优先级的。从内核角度讲[100,139]是普通进程的优先级的范围,100最高,139最低,默认是120。普通进程的优先级的作用和实时进程不同,普通进程优先级表示的是占的CPU时间。深入linux内核架构中提到,普通优先级越高(100最高,139最低),享受的CPU time越多,相邻的两个优先级,高一级的进程比低一级的进程多占用10%的CPU,比如内核优先级数值为120的进程要比数值是121的进程多占用10%的CPU。

    内核中有一个数组:prio_to_weight[20]表示的是默认优先级120的权重,数值为1024,prio_to_weight[21]表示nice值为1,优先级为121的进程的权重,数值为820。这就到了CFS的原理了

  1. static const int prio_to_weight[40]= {
  2.  /* -20 */ 88761, 71755, 56483, 46273, 36291,
  3.  /* -15 */ 29154, 23254, 18705, 14949, 11916,
  4.  /* -10 */ 9548, 7620, 6100, 4904, 3906,
  5.  /* -5 */ 3121, 2501, 1991, 1586, 1277,
  6.  /* 0 */ 1024, 820, 655, 526, 423,
  7.  /* 5 */ 335, 272, 215, 172, 137,
  8.  /* 10 */ 110, 87, 70, 56, 45,
  9.  /* 15 */ 36, 29, 23, 18, 15,
  10. };
     假如有1台电脑,10个人玩,怎么才公平。
    1 约定好时间片,每人玩1小时,玩完后记账,张XX 1小时,谁玩的时间短,谁去玩
    2 引入优先级的概念,李四有紧急情况,需要提高他玩电脑的时间,怎么办,玩1个小时,记账半小时,那么同等情况下,李四会比其他人被选中玩电脑的频率要高,就体现了这个优先级的概念。
    3  王五也有紧急情况,但是以考察,不如李四的紧急,好吧,玩1个小时,记账45分钟。
    4  情况有变化,听说这里有电脑,突然又来了10个人,如果按照每人玩1小时的时间片,排在最后的那哥们早就开始骂人了,怎么办?时间片动态变化,根据人数来确定时间片。人越多,每个人玩的时间越少,防止哥们老捞不着玩,耐心耗尽,开始骂人。

    这个记账就是我们prio_to_weight的作用。我就不多说了,prio_to_weight[20]就是基准,玩一小时,记账一小时,数组20以前的值是特权一级,玩1小时记账20分钟之类的享有特权的,数组20之后是倒霉蛋,玩1小时,记账1.5小时之类的倒霉蛋。 CFS这种调度好在大家都能捞着玩。

    扯到优先级多说了几句,现在回到正题。我将上面的C程序编译成可执行程序test,然后写了一个脚本comp.sh。

  1. [root@localhost sched]# cat comp.sh
  2. #/bin/sh
  3. ./test $1 99 &
  4. usleep 1000;
  5. ./test $1 70 &
  6. usleep 1000;
  7. ./test $1 70 &
  8. usleep 1000;
  9. ./test $1 70 &
  10. usleep 1000;
  11. ./test $1 50 &
  12. usleep 1000;
  13. ./test $1 30 &
  14. usleep 1000;
  15. ./test $1 10 &
       因为test进程有sleep 2秒,所以可以给comp.sh启动其他test的机会。可以看到有 99级(最高优先级)的实时进程,3个70级的实时进程,50级,30级,10级的各一个。
      对于FIFO而言,一旦sleep过后,高优先级运行,低优先级是没戏运行的,同等优先级的进程,先运行的不运行完,后运行的也没戏。
     对于RR而言,高优先级的先运行,同等优先级的进程过家家,你玩完,我玩,我玩完你再玩,每个进程耗费一个时间片的时间。对于Linux,RR时间片是100ms:

  1. #define DEF_TIMESLICE        (100* HZ / 1000)
  
    下面我们验证: 我写了两个观察脚本,来观察实时进程的调度情况:

    第一个脚本比较简单,观察进程的CPU 占用的time,用ps工具就可以了:

  1. [root@localhost sched]# cat getpsinfo.sh
  2. #!/bin/sh
  3. for((i = 0; i < 40; i++))
  4. do
  5.     ps -C test -o pid,pri,cmd,time,psr >>psinfo.log 2>&1
  6.     sleep 2;
  7. done
    第二个脚本比较复杂是systemtap脚本,观察名字为test的进程相关的上下文切换,谁替换了test,或者test替换了谁,同时记录下test进程的退出:

  1. [root@localhost sched]# cat cswmon_spec.stp

  2. global time_offset
  3. probe begin { time_offset = gettimeofday_us()}

  4. probe scheduler.ctxswitch {
  5.     if(next_task_name== "test" ||prev_task_name== "test")
  6.     {
  7.          t = gettimeofday_us()
  8.          printf(" time_off (%8d )%20s(%6d)(pri=%4d)(state=%d)->%20s(%6d)(pri=%4d)(state=%d)\n",
  9.                   t-time_offset,
  10.                   prev_task_name,
  11.                   prev_pid,
  12.                   prev_priority,
  13.                   (prevtsk_state),
  14.                   next_task_name,
  15.                   next_pid,
  16.                   next_priority,
  17.                   (nexttsk_state))
  18.     }
  19. }


  20. probe scheduler.process_exit
  21. {
  22.     if(execname()== "test")
  23.     printf("task :%s PID(%d) PRI(%d) EXIT\n",execname(),pid,priority);
  24. }

  25. probe timer.s($1){
  26.     printf("--------------------------------------------------------------\n")
  27.         exit();
  28. }
   

A)    FIFO调度策略的输出:
  1. 终端1 :
  2. stap ./cswmon_spec.stp 70
  3. 终端2 :
  4. ./getpsinfo.sh
  5. 终端3
  6. ./comp.sh 1
输出结果如下:
linux进程调度之 FIFO 和 RR 调度策略

    99优先级跑完了,才轮到70优先级,但是虽说有3个70优先级,但是先跑的那个进程跑完了,第二个优先级为70的才能跑。因为输出结果用代码无法漂亮的展示,所以我截了图,截图又不能把这个输出都截下来,所以我很蛋疼。有需要结果的,我以附件形式附在最后。

    看下第二个脚本的输出:

  1. time_off ( 689546 ) test( 6305)(pri= 120)(state=0)-> migration/2( 11)(pri= 0)(state=0)
  2. time_off ( 689977 ) stap( 5895)(pri= 120)(state=0)-> test( 6305)(pri= 120)(state=0)
  3. time_off ( 690067 ) test( 6305)(pri= 29)(state=1)-> stap( 5895)(pri= 120)(state=0)
  4. time_off ( 697899 ) test( 6303)(pri= 120)(state=0)-> migration/2( 11)(pri= 0)(state=0)
  5. time_off ( 698042 ) test( 6307)(pri= 120)(state=0)-> migration/0( 3)(pri= 0)(state=0)
  6. time_off ( 699114 ) stap( 5895)(pri= 120)(state=0)-> test( 6303)(pri= 120)(state=0)
  7. time_off ( 699307 ) test( 6303)(pri= 0)(state=1)-> test( 6307)(pri= 120)(state=0)
  8. time_off ( 699371 ) test( 6307)(pri= 29)(state=1)-> stap( 5895)(pri= 120)(state=0)
  9. time_off ( 699392 ) test( 6309)(pri= 120)(state=0)-> migration/3( 15)(pri= 0)(state=0)
  10. time_off ( 699966 ) events/1( 20)(pri= 120)(state=1)-> test( 6309)(pri= 120)(state=0)
  11. time_off ( 700034 ) test( 6309)(pri= 29)(state=1)-> stap( 5895)(pri= 120)(state=0)
  12. time_off ( 707379 ) test( 6311)(pri= 120)(state=0)-> migration/3( 15)(pri= 0)(state=0)
  13. time_off ( 707587 ) test( 6313)(pri= 120)(state=0)-> migration/0( 3)(pri= 0)(state=0)
  14. time_off ( 712021 ) stap( 5895)(pri= 120)(state=0)-> test( 6311)(pri= 120)(state=0)
  15. time_off ( 712145 ) test( 6311)(pri= 49)(state=1)-> test( 6313)(pri= 120)(state=0)
  16. time_off ( 712252 ) test( 6313)(pri= 69)(state=1)-> stap( 5895)(pri= 120)(state=0)
  17. time_off ( 727057 ) test( 6315)(pri= 120)(state=0)-> migration/0( 3)(pri= 0)(state=0)
  18. time_off ( 727952 ) stap( 5895)(pri= 120)(state=0)-> test( 6315)(pri= 120)(state=0)
  19. time_off ( 728047 ) test( 6315)(pri= 89)(state=1)-> stap( 5895)(pri= 120)(state=0)
  20. time_off ( 2690181 ) stap( 5895)(pri= 120)(state=0)-> test( 6305)(pri= 29)(state=0)
  21. time_off ( 2699316 ) test( 6305)(pri= 29)(state=0)-> test( 6303)(pri= 0)(state=0)
  22. task :test PID(6303) PRI(0) EXIT
  23. time_off (13057854 ) test( 6303)(pri= 0)(state=64)-> watchdog/1( 10)(pri= 0)(state=0)
  24. time_off (13057864 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6305)(pri= 29)(state=0)
  25. time_off (15333340 ) test( 6305)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  26. time_off (15333354 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6305)(pri= 29)(state=0)
  27. time_off (18743409 ) test( 6305)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  28. time_off (18743422 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6305)(pri= 29)(state=0)
  29. time_off (22154757 ) test( 6305)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  30. time_off (22154771 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6305)(pri= 29)(state=0)
  31. task :test PID(6305) PRI(29) EXIT
  32. time_off (22466855 ) test( 6305)(pri= 29)(state=64)-> test( 6307)(pri= 29)(state=0)
  33. time_off (25563548 ) test( 6307)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  34. time_off (25563566 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6307)(pri= 29)(state=0)
  35. time_off (28973602 ) test( 6307)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  36. time_off (28973616 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6307)(pri= 29)(state=0)
  37. task :test PID(6307) PRI(29) EXIT
  38. time_off (31846121 ) test( 6307)(pri= 29)(state=64)-> test( 6309)(pri= 29)(state=0)
  39. time_off (32383671 ) test( 6309)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  40. time_off (32383683 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6309)(pri= 29)(state=0)
  41. time_off (35793735 ) test( 6309)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  42. time_off (35793747 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6309)(pri= 29)(state=0)
  43. time_off (39203797 ) test( 6309)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  44. time_off (39203809 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6309)(pri= 29)(state=0)
  45. task :test PID(6309) PRI(29) EXIT
  46. time_off (41200440 ) test( 6309)(pri= 29)(state=64)-> test( 6311)(pri= 49)(state=0)
  47. time_off (42613866 ) test( 6311)(pri= 49)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  48. time_off (42613898 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6311)(pri= 49)(state=0)
  49. time_off (46024070 ) test( 6311)(pri= 49)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  50. time_off (46024082 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6311)(pri= 49)(state=0)
  51. time_off (49434004 ) test( 6311)(pri= 49)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  52. time_off (49434017 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6311)(pri= 49)(state=0)
  53. task :test PID(6311) PRI(49) EXIT
    可以清楚的可到,同样是70优先级(内核态是29),6305退出以前,6307根本就捞不着跑。同样6307退出一样,6309根本就捞不着跑。这就是FIFO。

B) RR的情况

  1. 终端1 :
  2. stap ./cswmon_spec.stp 70

  3. 终端2 :
  4. ./getpsinfo.sh

  5. 终端3
  6. ./comp.sh 1
linux进程调度之 FIFO 和 RR 调度策略

    实时优先级是70的三个进程齐头并进。再看第二个脚本的输出:

  1. time_off ( 4188015 ) test( 6428)(pri= 0)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  2. time_off ( 4188025 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6428)(pri= 0)(state=0)
  3. time_off ( 7612014 ) test( 6428)(pri= 0)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  4. time_off ( 7612024 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6428)(pri= 0)(state=0)
  5. task :test PID(6428) PRI(0) EXIT
  6. time_off (10679062 ) test( 6428)(pri= 0)(state=64)-> test( 6430)(pri= 29)(state=0)
  7. time_off (10964413 ) test( 6430)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  8. time_off (10964422 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6430)(pri= 29)(state=0)
  9. time_off (11709024 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  10. time_off (12736030 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  11. time_off (13779022 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  12. time_off (13879021 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  13. time_off (13984075 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  14. time_off (14084020 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  15. time_off (14184023 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  16. time_off (14284024 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  17. time_off (14374486 ) test( 6434)(pri= 29)(state=0)-> watchdog/1( 10)(pri= 0)(state=0)
  18. time_off (14374502 ) watchdog/1( 10)(pri= 0)(state=1)-> test( 6434)(pri= 29)(state=0)
  19. time_off (14384097 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  20. time_off (14484066 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  21. time_off (14584023 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  22. time_off (14684020 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  23. time_off (14786032 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  24. time_off (14886020 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  25. time_off (14986026 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  26. time_off (15089023 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  27. time_off (15192030 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  28. time_off (15292026 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  29. time_off (15396085 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  30. time_off (15496022 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
  31. time_off (15596027 ) test( 6434)(pri= 29)(state=0)-> test( 6430)(pri= 29)(state=0)
  32. time_off (15696153 ) test( 6430)(pri= 29)(state=0)-> test( 6432)(pri= 29)(state=0)
  33. time_off (15796022 ) test( 6432)(pri= 29)(state=0)-> test( 6434)(pri= 29)(state=0)
    用户态实时优先级为99,内核态优先级为0的进程6428退出后,3个用户态实时优先级为70的进程6430,6432,6434你方唱罢我登场,每个人都"唱"多久呢?看相邻2条记录的时间差,基本都在100ms左右,这就是时间片。

    后记:如果放开绑定到一个CPU的限制,同时加大实时进程的个数,多个实时进程在CPU之间PULL和PUSH,是更复杂的情况,呵呵,希望抛砖引玉,能有人模拟下这种情况。

    附件为测试代码及输出:
     linux进程调度之 FIFO 和 RR 调度策略 study_sched.txt  


参考文献
1 深入linux 内核架构
2 linux system program
3 systemtap example