操作系统:Pintos Project 1

也是借鉴了网上一些实现,勉勉强强将Project1过了,不过感觉代码中还是有些问题的。

alarm测试集

这个测试集要求重新实现timer_sleep()函数,将原来的忙等待改为非忙等待。
思路跟阻塞线程类似:

  • 1.为线程增加一个新的状态,表示线程正处在sleep中。
    thread.h:在文件靠前添加宏定义:
#define THREAD_SLEEP THREAD_BLOCKED
  • 2.在内核中增加一个列表sleep_list,用于存放处于THREAD_SLEEP状态的线程。
    thread.c:在文件靠前添加静态变量:
static struct list sleep_list;
  • 3.在struct thread结构体中添加两个成员变量,用于保存与THREAD_SLEEP状态相关的信息。
    thread.h:修改后的struct thread结构体:
struct thread
  {
    /* Owned by thread.c. */
    tid_t tid;                          /* Thread identifier. */
    enum thread_status status;          /* Thread state. */
    char name[16];                      /* Name (for debugging purposes). */
    uint8_t *stack;                     /* Saved stack pointer. */
    int priority;                       /* Priority. */
    struct list_elem allelem;           /* List element for all threads list. */

    /* Shared between thread.c and synch.c. */
    struct list_elem elem;              /* List element. */

    struct list_elem slpelem;//sleep_list的链表元素
    int64_t sleep_ticks;//还需要等待的ticks次数

#ifdef USERPROG
    /* Owned by userprog/process.c. */
    uint32_t *pagedir;                  /* Page directory. */
#endif

    /* Owned by thread.c. */
    unsigned magic;                     /* Detects stack overflow. */
  };
  • 4.为了实现非忙等待的timer_sleep(),需要实现几个函数,具体功能下文会解释。
    thread.h:在文件靠后添加函数声明:
void thread_sleep(int64_t ticks);
void thread_foreach_sleep (void);
bool thread_less_priority(const struct list_elem *a,const struct list_elem *b,void *aux UNUSED);
  • 5.实现thread_sleep()函数,该函数会将当前线程放入sleep_list队列中,并更新线程信息,最后调用schedule()进行新线程调度。该函数会在timer_sleep()中被调用。
    thread.c:在文件靠后添加以下代码:
void
thread_sleep(int64_t ticks)
{
  enum intr_level old_level=intr_disable();

  struct thread *t=thread_current();//获取当前线程
  t->sleep_ticks=ticks;//设置当前线程需要等待的ticks次数
  list_push_back (&sleep_list, &t->slpelem);//将当前线程放入sleep_list队列中
  t->status = THREAD_SLEEP;//更新当前线程的状态为THREAD_SLEEP
  schedule ();//进行线程调度,交出cpu,让其他线程继续执行

  intr_set_level(old_level);
}
  • 6.实现thread_foreach_sleep()函数,该函数会遍历sleep_list队列,并将其中的每个线程的sleep_ticks减1;若某个线程的sleep_ticks为0,即该线程不需要再继续等待,便将该线程从sleep_list队列移至就绪队列中。该函数会在timer_interrupt()中被调用。
    thread.c:在文件靠后添加以下代码:
void
thread_foreach_sleep (void)
{
  enum intr_level old_level=intr_disable();

  struct list_elem *e;

  for (e = list_begin (&sleep_list); e != list_end (&sleep_list);
       e = list_next (e))//遍历sleep_list中的每一个元素
    {
      struct thread *t = list_entry (e, struct thread, slpelem);//得到线程结构体
      t->sleep_ticks--;//更新sleep_ticks
      if(t->sleep_ticks==0)//如果还需等待的ticks为0,即sleep时间到了
      {
        list_remove(e);//将该线程从sleep_list队列中删除
        t->status = THREAD_READY;//将该线程的状态设置为THREAD_READY
        list_push_back (&ready_list, &t->elem);//将该线程放入就绪队列中
      }
    }

  intr_set_level(old_level);
}
  • 7.实现thread_less_priority()函数,该函数会比较传入线程a和b的优先级,若a的优先级小于b的,则返回真,否则返回假。该函数会在next_thread_to_run中被调用,用来从就绪队列中选择优先级最高的线程。
    thread.c:在文件靠后添加一下代码:
bool
thread_less_priority(const struct list_elem *a,const struct list_elem *b,void *aux UNUSED)
{
  return list_entry(a,struct thread,elem)->priority(b,struct thread,elem)->priority;
}
  • 8.修改timer_sleep()函数:一是要对输入参数进行合法性检查,输入参数ticks必须大于0才有意义;二是要将其改为非忙等待,看了上面的内容,其实思路已经很清楚了,只要调用thread_sleep()将当前线程扔到sleep_list队列中就行了。
    timer.c:修改后的timer_sleep()函数:
void
timer_sleep (int64_t ticks) 
{
  if(ticks<=0)
    return;

  thread_sleep(ticks);
}
  • 9.修改timer_interrupt()函数:当每个ticks中断到来时,需要对sleep_list中的线程进行更新,否则sleep中的线程将永远不会被唤醒。
    timer.c:修改后的timer_interrupt()函数:
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
  ticks++;
  thread_foreach_sleep();//增加对sleep_list中线程的更新
  thread_tick ();
}
  • 10.需要对新加入的sleep_list在系统启动后进行初始化,所以在thread_init函数中添加一行代码。
    thread.c:修改后的thread_init()函数:
void
thread_init (void) 
{
  ASSERT (intr_get_level () == INTR_OFF);

  lock_init (&tid_lock);
  list_init (&ready_list);
  list_init (&all_list);
  list_init (&sleep_list);//对sleep_list进行初始化

  /* Set up a thread structure for the running thread. */
  initial_thread = running_thread ();
  init_thread (initial_thread, "main", PRI_DEFAULT);
  initial_thread->status = THREAD_RUNNING;
  initial_thread->tid = allocate_tid ();
}
  • 11.收尾工作,因为project要求在调度下一个进程时,选择优先级最高的来执行,所以需要修改next_thread_to_run()函数。该函数原先选择就绪队列中的第一个线程来执行,现在我们利用库函数中提供的list_max()来选择其中优先级最高的线程。
    thread.c:修改后的next_thread_to_run()函数:
static struct thread *
next_thread_to_run (void) 
{
  struct list_elem *e;
  if (list_empty (&ready_list))
    return idle_thread;
  else
  {
    e=list_max (&ready_list,&thread_less_priority,NULL);//从前往后找到第一个优先级最高的线程
    list_remove(e);//从就绪队列中将选中的线程删去
    return list_entry (e, struct thread, elem);
  }
}

priority测试集

这个测试集主要是要求实现锁的优先级捐赠,及将cond改为按优先级选择下一线程,并且保证执行中的线程始终是优先级最高的。
因为原先的实现中是将lock视为值为1的信号量来处理的,为了实现优先级捐赠,需要用到其中的等待队列,但这样包裹一层结构体觉得很麻烦,所以对lock做了较大改动。

  • 1.为了实现优先级捐赠,我们先考虑struct thread需要增加哪些变量。我们需要记录当前线程的基础优先级,这样当捐赠结束时才能恢复原先的优先级;我们需要记录线程已经持有的锁,这样才能在需要的时候(获得或释放一个锁时),根据这些锁来决定新的捐赠的优先级大小;还需要记录线程正在等待的锁,这样当发生嵌套捐赠时,可以根据这个变量不断向前找到下一个被捐赠对象。
    thread.h:修改后的struct thread结构体:
struct thread
  {
    /* Owned by thread.c. */
    tid_t tid;                          /* Thread identifier. */
    enum thread_status status;          /* Thread state. */
    char name[16];                      /* Name (for debugging purposes). */
    uint8_t *stack;                     /* Saved stack pointer. */
    int priority;                       /* Priority. */
    struct list_elem allelem;           /* List element for all threads list. */

    /* Shared between thread.c and synch.c. */
    struct list_elem elem;              /* List element. */

    struct list_elem slpelem;
    int64_t sleep_ticks;

    struct lock *lock_waiting;//线程正在等待的锁
    struct list locks;//线程持有的锁
    int locks_priority;//线程持有的锁locks中的最高优先级
    int base_priority;//线程的基础优先级

#ifdef USERPROG
    /* Owned by userprog/process.c. */
    uint32_t *pagedir;                  /* Page directory. */
#endif

    /* Owned by thread.c. */
    unsigned magic;                     /* Detects stack overflow. */
  };
  • 2.为了实现优先级捐赠,再考虑需要对struct lock做的修改。需要记录锁的持有者是哪个线程,这样当发生嵌套捐赠时,可以根据这个变量不断向前找到下一个被捐赠对象;需要记录等待该锁的线程,这样当该锁被释放时,可以从中选择下一个锁的持有者;需要记录当前锁的优先级,便于优先级捐赠的实现。
    synch.h:修改后的struct lock结构体:
struct lock 
  {
    struct thread *holder;//持有当前锁的线程
    struct list waiters;//等待当前锁的线程列表
    int priority;//当前锁的优先级,就是waiters中最高的优先级
    struct list_elem elem;//struct thread中locks的链表元素
  };
  • 3.锁在初始化时,是没有优先级的,所以需要增加一个无效的优先级。
    thread.h:在文件靠前添加宏定义:
#define PRI_UNVALID -1
  • 4.对锁进行初始化。
    synch.c:修改后的lock_init()函数:
void
lock_init (struct lock *lock)
{
  ASSERT (lock != NULL);

  lock->holder = NULL;
  list_init (&lock->waiters);
  lock->priority=PRI_UNVALID;
}
  • 5.修改线程的初始化函数。
    thread.c:修改后的init_thread()函数:
static void
init_thread (struct thread *t, const char *name, int priority)
{
  ASSERT (t != NULL);
  ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
  ASSERT (name != NULL);

  memset (t, 0, sizeof *t);
  t->status = THREAD_BLOCKED;
  strlcpy (t->name, name, sizeof t->name);
  t->stack = (uint8_t *) t + PGSIZE;
  t->priority = priority;

  t->lock_waiting=NULL;
  list_init(&t->locks);
  t->locks_priority=PRI_UNVALID;
  t->base_priority=priority;

  t->magic = THREAD_MAGIC;
  list_push_back (&all_list, &t->allelem);
}
  • 6.为了实现优先级(嵌套)捐赠,需要实现几个函数,具体功能下文会解释。
    thread.h:在文件靠后添加函数声明:
void thread_priority_donate_nest(struct thread *t);
void thread_update_priority(struct thread *t);
void lock_update_priority(struct lock *l);
  • 7.实现thread_priority_donate_nest()函数,该函数实现了嵌套优先级捐赠,从传入的线程t开始,逐级向上进行优先级捐赠,且当无法进行捐赠时,及时推出循环。该函数会在lock_acquire()中被调用,因为只有当请求的锁被占用时,才会发生优先级捐赠。
    thread.c:在文件靠后添加以下代码:
void
thread_priority_donate_nest(struct thread *t)
{
  struct lock *l = t->lock_waiting;//获取当前线程等待的锁
  while(l){
    if(t->priority > l->priority)//判断当前线程是否能提高等待锁的优先级
      l->priority = t->priority;//若能,则进行优先级捐赠
    else
      break;//若不能,则结束优先级捐赠
    t = l->holder;//获得占有锁的线程
    if(l->priority > t->locks_priority)//判断当前锁是否能提高占有线程的锁优先级
      t->locks_priority = l->priority;//若能,则进行优先级捐赠
    else
      break;//若不能,则结束优先级捐赠
    if(l->priority > t->priority)//因为锁优先级可能低于优先级(因为基础优先级很高),所以这里需要再判断一次
      t->priority = l->priority;
    else
      break;
    l = t->lock_waiting;
  }
}
  • 8.实现thread_update_priority()函数,该函数根据线程持有的锁更细线程的锁优先级,并与基础优先级比较来更新优先级。
    thread.c:在文件靠后添加以下代码:
void
thread_update_priority(struct thread *t)
{
  t->locks_priority = PRI_UNVALID;

  struct lock *l;
  struct list_elem *e;
  for (e = list_begin (&t->locks); e != list_end (&t->locks);
       e = list_next (e))//遍历线程持有的锁
    {
      l = list_entry(e, struct lock, elem);
      if(l->priority > t->locks_priority)
        t->locks_priority = l->priority;//找到其中最高的优先级作为锁优先级
    }

  if(t->base_priority > t->locks_priority)//更新优先级
    t->priority = t->base_priority;
  else
    t->priority = t->locks_priority;
}
  • 9.实现lock_update_priority()函数,该函数根据等待线程来更新锁的优先级。这个函数本来想写在synch.c文件中,但是cmake中文件的依赖顺序的原因,会造成编译错误。
    thread.c:在文件靠后添加以下代码:
void
lock_update_priority(struct lock *l)
{
  l->priority = PRI_UNVALID;

  struct thread *t;
  struct list_elem *e;
  for (e = list_begin (&l->waiters); e != list_end (&l->waiters);
       e = list_next (e))//遍历等待该锁的线程
    {
      t = list_entry(e, struct thread, elem);
      if(t->priority > l->priority)
        l->priority = t->priority;//找到其中最高的优先级
    }
}
  • 10.修改lock_acquire()函数,实现优先级捐赠:首先判断锁是否被占用,若被占用,则进行优先级捐赠并将当前线程扔到阻塞队列中;若不被占用,便获取该锁,并更新相关的状态信息。
    synch.c:修改后的lock_acquire()函数:
void
lock_acquire (struct lock *lock)
{
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (!lock_held_by_current_thread (lock));

  struct thread *cur_t;
  enum intr_level old_level;

  old_level = intr_disable ();
  while (lock->holder != NULL) //判断请求的锁是否被占用
    {//锁被占用
      cur_t = thread_current();
      list_push_back (&lock->waiters, &cur_t->elem);//将当前线程放入锁的等待队列中
      cur_t->lock_waiting = lock;//设置当前线程正等待该锁
      thread_priority_donate_nest(cur_t);//进行优先级捐赠
      thread_block ();//将当前线程扔到就阻塞列中
    }

  cur_t = thread_current();
  lock->holder = cur_t;//当前线程获得该锁
  cur_t->lock_waiting = NULL;
  list_push_back(&cur_t->locks,&lock->elem);//将该锁放入当前线程的持有锁队列中
  if(lock->priority > cur_t->locks_priority)//因为有新锁加入,所以要更新优先级
    cur_t->locks_priority = lock->priority;
  if(lock->priority > cur_t->priority)//理由同上
    cur_t->priority = lock->priority;
  intr_set_level (old_level);
}
  • 11.修改lock_try_acquire()函数,实现优先级捐赠:思路跟上面的一样,只不过这里当锁被占用时,不会进入阻塞状态,而是返回false。
    synch.c:修改后的lock_try_acquire()函数:
bool
lock_try_acquire (struct lock *lock)
{
  bool success;

  ASSERT (lock != NULL);
  ASSERT (!lock_held_by_current_thread (lock));

  struct thread *cur_t;
  enum intr_level old_level;

  old_level = intr_disable ();
  if (lock->holder == NULL)
    {
      cur_t = thread_current();
      lock->holder = cur_t;
      cur_t->lock_waiting = NULL;
      list_push_back(&cur_t->locks,&lock->elem);
      if(lock->priority > cur_t->locks_priority)
        cur_t->locks_priority = lock->priority;
      if(lock->priority > cur_t->priority)
        cur_t->priority = lock->priority;
      success = true; 
    }
  else
    success = false;
  intr_set_level (old_level);

  return success;
}
  • 12.修改lock_release()函数,实现优先级捐赠中,当锁被释放时,还原优先级的操作。
    synch.c:修改后的lock_release()函数:
void
lock_release (struct lock *lock) 
{
  ASSERT (lock != NULL);
  ASSERT (lock_held_by_current_thread (lock));

  struct list_elem *e;
  struct thread *t;
  enum intr_level old_level;

  old_level = intr_disable();
  lock->holder = NULL;//将锁的持有者设为NULL,释放锁
  list_remove(&lock->elem);//同时将该锁从线程的持有锁队列中删除
  thread_update_priority(thread_current());//因为线程的锁队列发生变化,所以需要更新线程的优先级
  if(!list_empty(&lock->waiters))
  {//如果锁的等待队列不空
    e = list_max(&lock->waiters,&thread_less_priority,NULL);//从锁的等待队列中找到优先级最高的线程
    list_remove(e);//将该线程从锁的等待队列中移出
    lock_update_priority(lock);//因为锁的等待队列发生变化,所以需要更新所的优先级
    t = list_entry(e,struct thread,elem);
    thread_unblock(t);//将选出的线程放入到就绪队列中
  }
  thread_yield();//因为在释放锁的过程中,当前线程的优先级可能不再是所有线程中优先级最高的线程,所以需要交出cpu进行一次线程调度
  intr_set_level(old_level);
}
  • 13.做到这里,关于lock的优先级捐赠已经完成了。在线程调度中,有要求当前执行的线程一定所有线程中优先级最高的,所以需要在线程优先级发生发生变化的地方进行优先级调度。首先修改thread_create()函数,因为新创建的线程可能具有更高的优先级,所以需要进行线程调度,以使新线程执行。
    thread.c:修改后的thread_create()函数:
tid_t
thread_create (const char *name, int priority,
               thread_func *function, void *aux) 
{
  /*
  前面代码太长了,这里省了,要加的就是下面两行
  */

  if(priority > thread_current()->priority)
    thread_yield();

  return tid;
}
  • 14.修改thread_set_priority()函数,因为该函数也会修改线程的优先级,不过需要注意这里修改的是线程的基础优先级,所以还需要另加判断进行优先级更新。
    thread.c:修改后的thread_set_priority()函数:
void
thread_set_priority (int new_priority) 
{
  struct thread *t;
  enum intr_level old_level;

  old_level = intr_disable();
  t = thread_current();
  t->base_priority = new_priority;
  if(t->base_priority > t->locks_priority)
    t->priority = t->base_priority;
  else
    t->priority = t->locks_priority;
  thread_yield();
  intr_set_level(old_level);
}
  • 15.到目前为止,应该能够通过关于lock的大部分test。下面来修改semaphore的相关操作,保证在每次信号量增加时,选择的是等待队列中优先级最高的线程。
    synch.c:修改后的sema_up()函数:
void
sema_up (struct semaphore *sema) 
{
  struct list_elem *e;
  struct thread *t;
  enum intr_level old_level;

  ASSERT (sema != NULL);

  old_level = intr_disable ();
  if (!list_empty (&sema->waiters))
  {//如果等待队列非空,从中选择优先级最高的线程放入到就绪队列中
    e = list_max(&sema->waiters, &thread_less_priority, NULL);
    list_remove(e);
    t = list_entry(e, struct thread, elem);
    thread_unblock(t);
  }
  sema->value++;
  intr_set_level (old_level);

  thread_yield();
}
  • 16.剩下的就是实现cond的优先级版本了。这里,看了下cond的代码实现,感觉cond和semaphore其实没有太大差别,仅仅是cond在使用时增加了一层锁来保证原子操作。所以,这里直接全盘修改原来的实现,使用semaphore来实现cond。
    synch.h:修改后的struct condition结构体:
struct condition 
  {
    struct semaphore sema;
  };
  • 17.修改cond_init()函数,利用semaphore实现。
    synch.c:修改后的cond_init()函数:
void
cond_init (struct condition *cond)
{
  ASSERT (cond != NULL);

  sema_init(&cond->sema,0);
}
  • 18.修改cond_wait()函数,利用semaphore实现。
    synch.c:修改后的cond_wait()函数:
void
cond_wait (struct condition *cond, struct lock *lock) 
{
  ASSERT (cond != NULL);
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (lock_held_by_current_thread (lock));

  lock_release (lock);
  sema_down (&cond->sema);
  lock_acquire (lock);
}
  • 19.修改cond_signal()函数,利用semaphore实现。
    synch.c:修改后的cond_signal()函数:
void
cond_signal (struct condition *cond, struct lock *lock UNUSED) 
{
  ASSERT (cond != NULL);
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (lock_held_by_current_thread (lock));

  if (!list_empty (&cond->sema.waiters)) 
    sema_up(&cond->sema);
}
  • 20.修改cond_broadcast()函数,利用semaphore实现。
    synch.c:修改后的cond_broadcast()函数:
void
cond_broadcast (struct condition *cond, struct lock *lock) 
{
  ASSERT (cond != NULL);
  ASSERT (lock != NULL);

  while (!list_empty (&cond->sema.waiters))
    cond_signal (cond, lock);
}

mlfqs测试集

这个测试集要求实现多级队列反馈调度,具体的计算公式指导书中都已经给的很详细了,如何用整数运算模拟浮点运算在附录中也有说明。虽然叫做多级队列,但说到底每次还是选择优先级最高的线程来执行,所以只要确保线程的优先级范围正确,使用一个队列来实现就够了。
需要注意的是,虽然指导书中说priority测试集与mlfqs测试集的实现不会冲突,但实际上在这部分中有调用lock,所以为了能够全部pass,还需要修改原先的lock的操作。

  • 1.修改lock_acquire()函数,当进行mlfqs测试时,禁止修改线程优先级。
    synch.c:修改后的lock_acquire()函数:
void
lock_acquire (struct lock *lock)
{
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (!lock_held_by_current_thread (lock));

  struct thread *cur_t;
  enum intr_level old_level;

  old_level = intr_disable ();
  while (lock->holder != NULL) 
    {
      cur_t = thread_current();
      list_push_back (&lock->waiters, &cur_t->elem);
      cur_t->lock_waiting = lock;
      if(thread_mlfqs==false)
        thread_priority_donate_nest(cur_t);
      thread_block ();
    }

  cur_t = thread_current();
  lock->holder = cur_t;
  cur_t->lock_waiting = NULL;
  list_push_back(&cur_t->locks,&lock->elem);
  if(thread_mlfqs==false)
  {
    if(lock->priority > cur_t->locks_priority)
      cur_t->locks_priority = lock->priority;
    if(lock->priority > cur_t->priority)
      cur_t->priority = lock->priority;
  }
  intr_set_level (old_level);
}
  • 2.修改lock_try_acquire()函数,当进行mlfqs测试时,禁止修改线程优先级。
    synch.c:修改后的lock_try_acquire()函数:
bool
lock_try_acquire (struct lock *lock)
{
  bool success;

  ASSERT (lock != NULL);
  ASSERT (!lock_held_by_current_thread (lock));

  struct thread *cur_t;
  enum intr_level old_level;

  old_level = intr_disable ();
  if (lock->holder == NULL)
    {
      cur_t = thread_current();
      lock->holder = cur_t;
      cur_t->lock_waiting = NULL;
      list_push_back(&cur_t->locks,&lock->elem);
      if(thread_mlfqs==false)
      {
        if(lock->priority > cur_t->locks_priority)
          cur_t->locks_priority = lock->priority;
        if(lock->priority > cur_t->priority)
          cur_t->priority = lock->priority;
      }
      success = true; 
    }
  else
    success = false;
  intr_set_level (old_level);

  return success;
}
  • 3.修改lock_release()函数,当进行mlfqs测试时,禁止修改线程优先级。
    synch.c:修改后的lock_release()函数:
void
lock_release (struct lock *lock) 
{
  ASSERT (lock != NULL);
  ASSERT (lock_held_by_current_thread (lock));

  struct list_elem *e;
  struct thread *t;
  enum intr_level old_level;

  old_level = intr_disable();
  lock->holder = NULL;
  list_remove(&lock->elem);
  if(thread_mlfqs==false)
    thread_update_priority(thread_current());
  if(!list_empty(&lock->waiters))
  {
    e = list_max(&lock->waiters,&thread_less_priority,NULL);
    list_remove(e);
    lock_update_priority(lock);
    t = list_entry(e,struct thread,elem);
    thread_unblock(t);
  }
  thread_yield();
  intr_set_level(old_level);
}
  • 4.为了实现多级队列反馈调度,需要在struct thread结构体中添加新的变量,具体含义见指导书。
    thread.h:修改后的struct thread结构体:
struct thread
  {
    /* Owned by thread.c. */
    tid_t tid;                          /* Thread identifier. */
    enum thread_status status;          /* Thread state. */
    char name[16];                      /* Name (for debugging purposes). */
    uint8_t *stack;                     /* Saved stack pointer. */
    int priority;                       /* Priority. */
    struct list_elem allelem;           /* List element for all threads list. */

    /* Shared between thread.c and synch.c. */
    struct list_elem elem;              /* List element. */

    struct list_elem slpelem;
    int64_t sleep_ticks;

    struct lock *lock_waiting;
    struct list locks;
    int locks_priority;
    int base_priority;

    int nice;
    int recent_cpu;//用整数模拟的浮点数

#ifdef USERPROG
    /* Owned by userprog/process.c. */
    uint32_t *pagedir;                  /* Page directory. */
#endif

    /* Owned by thread.c. */
    unsigned magic;                     /* Detects stack overflow. */
  };
  • 5.修改init_thread()函数,对新添加的变量进行初始化。
    thread.c:修改后的init_thread()函数:
static void
init_thread (struct thread *t, const char *name, int priority)
{
  ASSERT (t != NULL);
  ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
  ASSERT (name != NULL);

  memset (t, 0, sizeof *t);
  t->status = THREAD_BLOCKED;
  strlcpy (t->name, name, sizeof t->name);
  t->stack = (uint8_t *) t + PGSIZE;
  t->priority = priority;

  t->lock_waiting=NULL;
  list_init(&t->locks);
  t->locks_priority=PRI_UNVALID;
  t->base_priority=priority;

  t->nice = 0;//设置nice初值为0
  t->recent_cpu = 0;//设置recent_cpu初值为0

  t->magic = THREAD_MAGIC;
  list_push_back (&all_list, &t->allelem);
}
  • 6.修改thread_create()函数,对新创建的线程设置其nice与recent_cpu的值与父线程相等。
    thread.c:修改后的thread_create()函数:
tid_t
thread_create (const char *name, int priority,
               thread_func *function, void *aux) 
{
  struct thread *t;
  struct kernel_thread_frame *kf;
  struct switch_entry_frame *ef;
  struct switch_threads_frame *sf;
  tid_t tid;
  enum intr_level old_level;

  ASSERT (function != NULL);

  /* Allocate thread. */
  t = palloc_get_page (PAL_ZERO);
  if (t == NULL)
    return TID_ERROR;

  /* Initialize thread. */
  init_thread (t, name, priority);
  struct thread *cur_t = thread_current();
  t->nice = cur_t->nice;//创建的新线程的nice应该等于父线程的nice
  t->recent_cpu = cur_t->recent_cpu;//理由同上
  tid = t->tid = allocate_tid ();

  /*
  后面的代码不用修改
  */
}
  • 7.为了实现多级队列反馈调度,需要添加一全局变量,同时为了方便进行模拟浮点运算,在代码中添加一个宏。
    thread.c:在文件靠前添加以下代码:
#define fp_one (1<<14) //表示浮点数1.0
static int load_avg;
  • 8.修改thread_init()函数,在系统启动时对全局变量load_avg进行初始化。
    thread.c:修改后的thread_init()函数:
void
thread_init (void) 
{
  ASSERT (intr_get_level () == INTR_OFF);

  lock_init (&tid_lock);
  list_init (&ready_list);
  list_init (&all_list);
  list_init (&sleep_list);

  load_avg = 0;//全局变量初始化为0

  /* Set up a thread structure for the running thread. */
  initial_thread = running_thread ();
  init_thread (initial_thread, "main", PRI_DEFAULT);
  initial_thread->status = THREAD_RUNNING;
  initial_thread->tid = allocate_tid ();
}
  • 9.为计算线程的优先级,需要实现以下几个函数,具体功能下文会解释。
    thread.h:在文件靠后添加函数声明:
void thread_increase_recent_cpu(void);
void thread_recalculate_load_avg(void);
void thread_recalculate_recent_cpu(struct thread *t,void *);
void thread_recalculate_priority(struct thread *t,void *);
  • 10.上面的这四个函数的实现,具体解释见注释。在实现这几个函数的时候,要特别注意idle_thread线程带来的细微差别——我当时在写这部分代码时忘了这件事,结果怎么改都是错的,后来看了别人的实现才恍然大悟。
    thread.c:在文件靠后添加以下代码:
void
thread_increase_recent_cpu(void)//每一个tick都需要更新当前线程的recent_cpu
{
  struct thread *t = thread_current();
  if(t!=idle_thread)
    t->recent_cpu = t->recent_cpu + fp_one;//浮点加1
}

void
thread_recalculate_load_avg(void)//每秒都需要更新全局变量load_avg
{
  int size=list_size(&ready_list);
  if(thread_current()!=idle_thread)
    size++;
  load_avg = load_avg*59/60 + (size)*fp_one/60;
}

void
thread_recalculate_recent_cpu(struct thread *t,void *aux UNUSED)//每秒都需要对所有线程重新计算recent_cpu
{
  if(t==idle_thread)
    return;
  t->recent_cpu = (((int64_t)(2*load_avg))*fp_one/(2*load_avg+fp_one))
                   *t->recent_cpu/fp_one+t->nice*fp_one;
}

void
thread_recalculate_priority(struct thread *t,void *aux UNUSED)//每4个ticks都需要对所有线程重新计算优先级
{
  if(t==idle_thread)
    return;
  t->priority = (PRI_MAX*fp_one - (t->recent_cpu / 4) 
                 - t->nice*2*fp_one) / fp_one;
  if(t->priority > PRI_MAX)
    t->priority = PRI_MAX;
  if(t->priority < PRI_MIN)
    t->priority = PRI_MIN;
}
  • 11.前面准备工作做了很多,下一步就是在特定的时间调用上面的函数,对线程的优先级进行更新并进行调度。这就需要获得系统从启动开始的ticks次数,那么最直接的方式就是修改timer_interrupt()函数。
    timer.c文件:修改后的timer_interrupt()函数:
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
  ticks++;

  if(thread_mlfqs)
  {
    thread_increase_recent_cpu();//每个ticks更新
    if(ticks % TIMER_FREQ == 0)//每秒更新
    {
      thread_recalculate_load_avg();
      thread_foreach(&thread_recalculate_recent_cpu,NULL);
    }
    if(ticks %4 == 0)//每4个ticks更新
      thread_foreach(&thread_recalculate_priority,NULL);
  }

  thread_foreach_sleep();
  thread_tick ();
}
  • 12.最后剩下的收尾工作,将代码中提供的几个模板函数实现了。
    thread.c:修改后的以下函数:
/* Sets the current thread's nice value to NICE. */
void
thread_set_nice (int nice) 
{
  struct thread *t = thread_current();
  t->nice = nice;
  thread_recalculate_priority(t,NULL);
  thread_yield();
}

/* 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) 
{
  int avg = load_avg;
  if(avg>=0)
    avg = (avg+fp_one/2)/fp_one;
  else
    avg = (avg-fp_one/2)/fp_one;
  return avg*100;
}

/* Returns 100 times the current thread's recent_cpu value. */
int
thread_get_recent_cpu (void) 
{
  int cpu = thread_current()->recent_cpu;
  if(cpu>=0)
    cpu = (cpu+fp_one/2)/fp_one;
  else
    cpu = (cpu-fp_one/2)/fp_one;
  return cpu*100;
}

你可能感兴趣的:(操作系统)