Linux2.6 内核进程调度分析


Linux2.6 内核进程调度分析


   进程的调度时机与引起进程调度的原因和进程调度的方式有关。在 2.6 中,除核心应用
    主动调用调度器之外, 核心还在应用不完全感知的情况下在以下三种时机中启动调度器工作:
    1>从中断或系统调用返回到用户态;
    2>某个进程允许被抢占 CPU;
    3>主动进入休眠状态;
  
  
 调度策略:
    在 Linux2.6 中,仍有三种调度策略: SCHED_OTHER、SCHED_FIFO 和 SCHED_RR。
  SCHED_ORHER:普通进程,基于优先级进行调度。
  SCHED_FIFO:实时进程,实现一种简单的先进先出的调度算法。
  SCHED_RR:实时进程,基于时间片的SCHED_FIFO,实时轮流调度算法。
    
前者是普通进程调度策略,后两者都是实时进程调度策略。
SCHED_FIFO 与 SCHED_RR 的区别是:
当进程的调度策略为前者时,当前实时进程将一直占用 CPU 直至自动退出,除非有更紧迫的、
优先级更高的实时进程需要运行时,它才会被抢占 CPU;当进程的调度策略
为后者时,它与其它实时进程以实时轮流算法去共同使用 CPU,用完时间片放到运行队列尾部。

注:实时进程的优先级高于普通进程,后面介绍。

     O(1)调度器是以进程的动态优先级 prio为调度依据的,它总是选择目前就绪队列中优先
级最高的进程作为候选进程 next。由于实时进程的优先级总是比普通进程的优先级高,故能
保证实时进程总是比普通进程先被调度。
     Linux2.6 中,优先级 prio 的计算不再集中在调度器选择 next 进程时,而是分散在进程
状态改变的任何时候,这些时机有:
     1>进程被创建时;
     2>休眠进程被唤醒时;
     3>从TASK_INTERRUPTIBLE 状态中被唤醒的进程被调度时;
     4>因时间片耗尽或时间片过长而分段被剥夺 CPU 时;
     在这些情况下,内核都会调用 effective_prio()重新计算进程的动态优先级 prio并根据计算结果调整它在就绪队列中的位置。
     
     
调度算法:
O(1)调度器的重要数据结构
     (1)就绪队列 struct runqueue
     runqueue 的设计是 O(1)调度器的关键技术所在,它用于存放特定 CPU 上的就绪进程队
列信息,其中包含每个 CPU 的调度信息。该结构在 /kernel/sched.c 中的定义如下:
     struct runqueue {
     ...
     prio_array_t *active, *expired, array[2];
     
      active 是指向活动进程队列的指针
      expired 是指向过期进程队列的指针
      array[2]是实际的优先级进程队列,其中一个是活跃的一个是过期的,过期数组存放时间片耗完的进程
     ...
     }
     
     
     在 2.6 中,每个 CPU 单独维护一个就绪队列,每个就绪队列都有一个自旋锁,从而解
     决了 2.4 中因只有一个就绪队列而造成的瓶颈。
     
     (2)task_struct 结构
     Linux2.6 内核使用 task_struct 结构来表示进程。2.6 对 task_struct 也做了较大的改动,
     该结构定义在/include/linux/sched.h 中:
     struct task_struct{
     ...
     int prio,static_prio;
     prio 是动态优先级,static_prio 是静态优先级(与最初nice相关)
     ...
     prio_array_t *array;
     记录当前 CPU 的活跃就绪队列 
     
     unsigned long sleep_avg;
     进程的平均等待时间,取值范围[0,MAX_SLEEP_AVG],初值为0。
     sleep_avg 反映了该进程需要运行的紧迫性。进程休眠该值增加,如果进程当前正在运行该值减少。
     是影响进程优先级最重要的元素。值越大,说明该进程越需要被调度。 
                                              
     ...
     };
   
     (3)优先级数组
     每个处理器的就绪队列都有两个优先级数组,它们是 prio_array 类型的结构体。Linux2.6
内核正是因为使用了优先级数组,才实现了 O(1)调度算法。该结构定义在 kernel/sched.c 中:
     struct prio_array{
     ...
     unsigned int nr_active;
     /**相应 runqueue 中的进程数
     
     unsigned long bitmap[BITMAP_SIZE];
     /**索引位图,BITMAP_SIZE 默认值为 5,5个long(32位)类型,每位代表一个优先级,可以代表160个优先级,但实际中只有140。
     与下面的queue[]对应。
     分布0-99对应为实时进程,100-140对应为普通的进程
     
     struct list_head queue[MAX_PRIO];
     /**每个优先级的进程队列,MAX_PRIO 是系统允许的最大优先级数,默认值为 140,数值越小优先级越高
     bitmap每一位都与 queue[i]相对应,当 queue[i]的进程队列不为空时,bitmap 相应位为 1,否则就为 0。
     }

     
 O(1)调度算法实现的简单介绍
     (1)选择并运行候选进程 next它确定下一个应该占有 CPU 并运行的进程,
     schedule()函数是完成进程调度的主要函数,
     并完成进程切换的工作。schedule()用于确定最高优先级进程的代码非常快捷高效,其

你可能感兴趣的:(Linux内核设计与实现,Linux内核设计与实现,Linux2.6,进程,调度,schedule,UNIX)