Linux2.6内核进程调度系列3.更新普通进程的时间片

时间:2022-12-22 21:16:29
origin: http://www.cnblogs.com/joey-hua/p/5792633.html
/**
  * 运行到此,说明进程是普通进程。现在开始更新普通进程的时间片。
  */
  /* 首先递减普通进程的时间片计数器。如果用完,继续执行以下操作 */
if (!--p->time_slice) {
     /**
      * 既然用完了,就将当前进程从活动集合中摘除。
      */
     dequeue_task(p, rq->active);
     /**
      * 当然,当前进程既然已经过期,就必须设置重新调度标志,
      * 以便在中断返回前调用schedule选择另外一个进程来运行。
      */
     set_tsk_need_resched(p);
     /**
      * 更新当前进程的动态优先级。
      * effective_prio根据当前进程的static_prio和sleep_avg字段,
      * 计算进程的动态优先级。
      */
     p->prio = effective_prio(p);
     /**
      * 重填进程的时间片
      */
     p->time_slice = task_timeslice(p);
     /**
      * 既然当前进程的一个时间片已经用完,
      * 当然就需要清除first_time_slice标志了。
      */
     p->first_time_slice = 0;
 
     /**
      * 如果本地运行队列的expired_timestamp为0,表示过期进程集合为空。
      * 并且当前进程马上就会变成过期进程,
      * 那么将当前jiffies赋给expired_timestamp
      * expired_timestamp表示当前队列中,过期队列中
      * 最老进程被插入过期队列的时间。
      */
     if (!rq->expired_timestamp)
         rq->expired_timestamp = jiffies;
     /**
      * 把当前进程插入过期进程集合或者活动进程集合。
      * TASK_INTERACTIVE判断当前进程是否是一个交互式进程。
      * TASK_INTERACTIVE宏检查运行队列中的第一个过期进程
      * 的等待时间是否已经超过1000个时钟节拍乘以运行队列
      * 中的可运行进程数+1,如果是返回1.
      * EXPIRED_STARVING表示如果当前进程的静态优先级大于
      * 过期进程的静态优先级,也返回1.
      */
     if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) {
         /**
          * 当前进程不是交互式进程,或者过期队列中有优先级
          * 更高的进程,那么将当前进程插入到过期队列。
          */
         enqueue_task(p, rq->expired);
         /**
          * 如果当前进程是过期队列中优先级最高的低,
          * 就更新过期队列的最高优先级。
          */
         if (p->static_prio < rq->best_expired_prio)
             rq->best_expired_prio = p->static_prio;
     } else
     /* 进程是交互式进程,并且比过期队列中所有进程的静态优先级高,
     * 那么就将它加到活动队列中。这实际上是对交互式进程的优待。 */
         enqueue_task(p, rq->active);
}
else { /* 普通进程的时间片还没有用完,需要进一步检查是否时间片太长 */
     
     /**
      * 检查当前进程的时间片是否太长,因为对于交互式进程来说,
      * 它时间片用完后,可能会再插入到活动队列,可能导致这种
      * 进程的时间片特别长。
      */
     if (TASK_INTERACTIVE(p) && !((task_timeslice(p) -
         p->time_slice) % TIMESLICE_GRANULARITY(p)) &&
         (p->time_slice >= TIMESLICE_GRANULARITY(p)) &&
         (p->array == rq->active)) {
 
         requeue_task(p, rq->active);
         set_tsk_need_resched(p);
     }
}

1.effective_prio函数计算进程的动态优先级。

普通进程除了静态优先级,还有动态优先级,其值的范围是100(最高优先级)~139(最低优先级)。动态优先级是调度程序在选择新进程来运行的时候使用的数。它与静态优先级的关系用下面的经验公式表示:

Linux2.6内核进程调度系列3.更新普通进程的时间片

bonus是范围从0-10的值,值小于5表示降低动态优先级以示惩罚,值大于5表示增加动态优先级以示奖赏。bonus的值依赖于进程过去的情况,说得更准确一些,是与进程的平均睡眠时间相关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define CURRENT_BONUS(p) \
     (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / \
         MAX_SLEEP_AVG)
 
/**
  * 读取current的static_prio和sleep_avg字段,并根据公司计算进程的动态优先级。
  */
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;
}

2.TASK_INTERACTIVE宏判断进程是不是一个交互式进程。

粗略地讲,平均睡眠时间是进程在睡眠状态所消耗的平均纳秒数。注意,这绝对不是对过去时间的求平均值的操作。例如,在TASK_INTERRUPTIBLE状态与在TASK_UNINTERRUPTIBLE状态所计算出的平均睡眠时间是不同的。而且,进程在运行的过程中平均睡眠时间递减。最后,平均睡眠时间永远不会大于1s。

平均睡眠时间也被调度程序用来确定一个给定进程是交互式进程还是批处理进程。更明确地说,如果一个进程满足下面的公式,就被看作是交互式进程:

Linux2.6内核进程调度系列3.更新普通进程的时间片

它相当于下面的公式:

Linux2.6内核进程调度系列3.更新普通进程的时间片

1
2
3
4
5
#define DELTA(p) \
     (SCALE(TASK_NICE(p), 40, MAX_BONUS) + INTERACTIVE_DELTA)
 
#define TASK_INTERACTIVE(p) \
     ((p)->prio <= (p)->static_prio - DELTA(p))