pintos (5) --Advanced Scheduler

多级反馈队列调度器(multilevel feedback queue scheduler)

在多级反馈队列调度器中,线程的优先级是通过下面的公式动态计算的:

priority = PRI_MAX - (recent_cpu/4) - (nice * 2)
recent_cpu = (2*load_avg)/(2*load_avg + 1)*recent_cpu + nice
load_avg= (59/60)*load_avg + (1/60)*ready_threads
  • priority即为线程的优先级,每4个timer tick,按上式计算一次。
  • nice为线程属性,取值[-20,+20],越大表示该线程出让更多的CPU时间。
  • recent_cpu表示线程消耗的timer tick,每一次timer tick中断,该值加1,每一秒,recent_cpu按照上式计算一次。
  • load_avg表示过去的一分钟内处于就绪状态的线程数,初始值为0,每一秒按上式计算一次。

公式计算的过程中,需要小数的支持,而pintos不支持浮点数运算,所以我们需要实现定点小数的运算。


首先,实现定点小数运算fixed_point.h

#ifndef __FIXED_POINT_H
#define __FIXED_POINT_H

typedef long long fp_t;

#define FD_SHIFT_AMOUNT 12

/*
 * X is a fp_t variable
 * N is a int variable
 */

#define FD_CONV_N(N) ((fp_t)((N)<<FD_SHIFT_AMOUNT))
#define FD_CONV_X_ZERO(X) ((X)>>FD_SHIFT_AMOUNT)
#define FD_CONV_X_ROUND(X) \
        ((X)>=0 ?\
                (((X)+(1<<(FD_SHIFT_AMOUNT-1)))>>FD_SHIFT_AMOUNT): \
                (((X)-(1<<(FD_SHIFT_AMOUNT-1)))>>FD_SHIFT_AMOUNT) )
#define FD_ADD(X, Y) ((X)+(Y))
#define FD_SUB(X, Y) ((X)-(Y))
#define FD_ADD_N(X, N) ((X) + (N<<FD_SHIFT_AMOUNT))
#define FD_SUB_N(X, N) ((X) - (N<<FD_SHIFT_AMOUNT))
#define FD_MUL(X, Y) (((fp_t)X)*(Y)>>FD_SHIFT_AMOUNT)
#define FD_MUL_N(X, N) ((X)*N)
#define FD_DIV(X, Y) ((((fp_t)X)<<FD_SHIFT_AMOUNT)/(Y))
#define FD_DIV_N(X, N) ((X)/N)


#endif

在thread.c中定义load_avg:

/* The average number of threads ready to run over the past minute.*/
static fp_t load_avg = 0;

在struct thread中增加nice 和 recent_cpu的定义:

fp_t recent_cpu;        /* Inc per ticks and recalculate per second.*/
int nice;               /* Between -20 to 20.*/

同时在init_thread()中对其进行初始化。

thread_tick()函数每个timer tick被调用一次,所以我们在其中完成:

  • 当前线程recent_cpu++
  • 每一秒,更新recent_cpu和load_avg
  • 每4个ticks,更新所有线程的priority
void
thread_tick (void) 
{
  struct thread *t = thread_current ();

  /* Update statistics. */
  if (t == idle_thread)
    idle_ticks++;
#ifdef USERPROG
  else if (t->pagedir != NULL)
    user_ticks++;
#endif
  else
    kernel_ticks++;

  /* Enforce preemption. */
  if (++thread_ticks >= TIME_SLICE)
    intr_yield_on_return ();

  /* Inc per ticks.*/
  if(t != idle_thread){
      t->recent_cpu = FD_ADD_N(t->recent_cpu, 1);
      //msg("%d %d",t, FD_CONV_X_ROUND(t->recent_cpu));
  }
  int ticks = timer_ticks();
  /* Calculate the load_avg and recent_cpu per second.*/
  if(thread_mlfqs && ticks % 100 == 0){
    size_t ready_threads = 0; /* The number of threads that are either running or ready. */
    ready_threads += list_size(&ready_list);
    if(t != idle_thread)
      ready_threads++;
    load_avg = FD_DIV_N(FD_ADD_N(FD_MUL_N(load_avg, 59),ready_threads), 60);

    enum intr_level old_level = intr_disable ();
    thread_foreach(thread_update_recent_cpu, NULL);
    intr_set_level (old_level);
  }

  /* Calculate priority per fourth tick.*/
  if(thread_mlfqs && ticks % 4 == 0){
    enum intr_level old_level = intr_disable ();
    thread_foreach(thread_update_priority, NULL);
    intr_set_level (old_level);
  }

  /* Find if a thread need to be awake from block
   * by block ticks over.
   */
  enum intr_level old_level = intr_disable ();
  thread_foreach(thread_dec_blockticks, NULL);
  intr_set_level (old_level);

}

其中thread_mlfqs为多级反馈调度指示变量,当前为true。
OK其实重新定义函数看起来会干净一些

thread_update_recent_cpu()实现如下:

/*
 * Calculate threads recent_cpu.
 */
void thread_update_recent_cpu(struct thread *t, void *aux){
    if(t == idle_thread) return;
    fp_t recent_cpu = t->recent_cpu;
    int nice = t->nice;
    //t->recent_cpu = (2*load_avg)/(2*load_avg+1)*recent_cpu + nice;
    t->recent_cpu = FD_ADD_N(FD_MUL(FD_DIV(FD_MUL_N(load_avg, 2), FD_ADD_N(FD_MUL_N(load_avg, 2), 1)), recent_cpu), nice);
}

thread_update_priority()实现如下:

/*
 * Calculate threads priority by multi-level feedback queue scheduler.
 */
void thread_update_priority(struct thread *t, void *aux){
    if(t == idle_thread) return;
    fp_t recent_cpu = t->recent_cpu;
    int nice = t->nice;
    //int priority = PRI_MAX - (recent_cpu/4) - (nice*2);
    int priority = FD_CONV_X_ROUND(FD_SUB(FD_CONV_N(PRI_MAX), FD_ADD_N(FD_DIV_N(recent_cpu, 4), (nice*2))));
    if(priority > PRI_MAX) priority = PRI_MAX;
    if(priority < PRI_MIN) priority = PRI_MIN;
    t->priority = priority;
}

之后还有四个辅助函数实现如下:

/* Sets the current thread's nice value to NICE. */
void
thread_set_nice (int nice UNUSED) 
{
    enum intr_level old_level = intr_disable ();
    thread_current ()->nice = nice;
    thread_update_priority(thread_current(), NULL);
    thread_yield();
    intr_set_level (old_level);
}

/* Returns the current thread's nice value. */
int
thread_get_nice (void) 
{
  return thread_current ()->nice;
}

/* Returns 100 times the system load average. */
int
thread_get_load_avg (void) 
{
  return FD_CONV_X_ROUND(FD_MUL_N(load_avg, 100));
}

/* Returns 100 times the current thread's recent_cpu value. */
int
thread_get_recent_cpu (void) 
{
  return FD_CONV_X_ROUND(FD_MUL_N(thread_current()->recent_cpu, 100));
}

需要注意的是设置nice值后线程的优先级发生了改变,需要重新计算优先级并调度。

由于多级反馈队列调度不与优先级捐赠同时存在,所以有关优先级捐赠的部分均使用thread_mlfqs变量进行判断,在此不再赘述。


mlfqs-block测试中,要求阻塞在一个锁的线程在锁被释放的时候立即被调度,所以需要将Mesa style的sema改为Hoare style:

void
sema_up (struct semaphore *sema) 
{
  enum intr_level old_level;

  ASSERT (sema != NULL);

  old_level = intr_disable ();
  if (!list_empty (&sema->waiters)) 
    thread_unblock (list_entry (list_pop_front (&sema->waiters),
                                struct thread, elem));
  sema->value++;
  intr_set_level (old_level);
  thread_yield();
}

在sema_up完成时进行一次调度即可。


通过测试:mlfqs-*

至此,threads全部27个测试通过:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值