pintos (3) --Priority Inversion

解決lock上優先級反轉的問題

  • 優先級反轉的問題使用優先級捐贈來解決,噹噹前線程想要獲得低優先級線程擁有的鎖時,將低優先級線程的優先級設爲當前線程的優先級,當低優先級線程釋放鎖的時候恢復其原始優先級。
  • 整個過程分兩個部分:捐贈和恢復。
  • 其中的問題有:鏈式捐贈,多重捐贈,捐贈對信號量的影響。

優先級捐贈分兩步:

  1. 如果要獲取的鎖已被獲取,且擁有者的優先級低於當前線程,則設置其優先級爲當前線程優先級。此時存在捐贈鏈的情況。
  2. 在釋放鎖的時候,恢復初始優先級。此時存在被多個鎖捐贈的情況。

在struct thread中:

爲了恢復基礎優先級,我們將其保存:
int base_priority; /* Base priority. */
爲了解決被多鎖捐贈的情況,我們保存當前線程獲取到的所有鎖:
struct list lock_list; /* Acquired locks.*/
爲了解決鏈式捐贈的問題,我們增加指向被當前線程捐贈優先級的線程指針:
struct thread *donate_to; /* Donate priority to this thread.*/

同時在init_thread()中初始化它們。

爲了維護lock_list,我們修改struct lock,增加list_elem:

/* Lock. */
struct lock 
  {
    struct thread *holder;      /* Thread holding lock (for debugging). */
    struct semaphore semaphore; /* Binary semaphore controlling access. */
    struct list_elem elem;      /* List element.*/
  };

對1的實現在lock_acquire()函數中:

void
lock_acquire (struct lock *lock)
{
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (!lock_held_by_current_thread (lock));

  /* If lock is held and the holder's priority below current thread,
   * set it's priority equal current thread.*/
  struct thread *t = thread_current();
  if(!thread_mlfqs && lock->holder != NULL && t->priority > lock->holder->priority){
      t->donate_to = lock->holder;
      /* Donate chain. */
      while(t->donate_to != NULL){
        t->donate_to->priority = t->priority;
        t = t->donate_to;
      }
  }

  sema_down (&lock->semaphore);
  lock->holder = thread_current ();
  list_push_back(&thread_current()->lock_list, &lock->elem);
}

如果要捐贈的線程同時也捐贈給其他線程優先級,則鏈式的捐贈下去。
若成功獲取該鎖,則將該鎖加入到當前線程的lock_list中。

注:thread_mlfqs爲多級反饋調度的指示變量,當前可看作false。

lock_try_acquire()則只需要維護lock_list:

bool
lock_try_acquire (struct lock *lock)
{
  bool success;

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

  success = sema_try_down (&lock->semaphore);
  if (success){
    lock->holder = thread_current ();
    list_push_back(&thread_current()->lock_list, &lock->elem);
  }

  return success;
}

對2的實現在lock_release()函數中:

void
lock_release (struct lock *lock) 
{
  ASSERT (lock != NULL);
  ASSERT (lock_held_by_current_thread (lock));


  /*
   * */
  struct thread *t = thread_current();
  struct list_elem *e;
  int max_priority = t->base_priority;
  /* Iterator all locks.*/
  for (e = list_begin (&t->lock_list); e != list_end (&t->lock_list); e = list_next (e)){
      struct lock *l = list_entry(e, struct lock, elem);
      if(l == lock){
          /* This lock will be release,
           * so we should get the max priority from other locks.*/
          continue;
      }
      struct list *waiters = &l->semaphore.waiters;
      /* Iterator this lock's waiters.*/
      if(list_size(waiters) != 0){
          int p = list_entry(list_front(waiters), struct thread, elem)->priority;
          if(p > max_priority){
              max_priority = p;
          }
      }
  }
  /* Release this lock from lock_list.*/
  list_remove(&lock->elem);
  /* Clear donate_to.*/
  if(!list_empty(&lock->semaphore.waiters))
      list_entry(list_front(&lock->semaphore.waiters), struct thread, elem)->donate_to = NULL;

  lock->holder = NULL;
  if(!thread_mlfqs){
      t->priority = max_priority;
  }
  sema_up (&lock->semaphore);
}

首先將要釋放的lock從lock_list中排除掉

  • 如果lock_list仍不爲空,說明當前線程仍被其他lock的等待線程捐贈優先級,故從其中找出優先級最高的線程,將其優先級設爲當前線程優先級
  • 如果lock_list爲空,則直接恢復base_priority。

最後,將要釋放的lock從當前線程的lock_list中刪除,以及設置捐贈線程的donate_to爲NULL。


設置優先級的時候,如果當前線程優先級被捐贈,則只設置base_priority:

void
thread_set_priority (int new_priority) 
{
    enum intr_level old_level = intr_disable ();
    struct thread *t = thread_current();
    if(t->priority == t->base_priority){
        /* Not been donated.*/
        t->priority = new_priority;
    }else if(new_priority > t->priority){
        t->priority = new_priority;
    }
    t->base_priority = new_priority;

    thread_yield();
    intr_set_level (old_level);
}

還存在的一個問題是,我們之前維護sema waiters list 爲按優先級排列,當waiters中的線程優先級被捐贈之後,sema_up()就不能保證喚醒的是優先級最高的線程了,所以我們在喚醒前,要將waiters list重新排序:

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

  ASSERT (sema != NULL);

  old_level = intr_disable ();
  /* The thread's priority may be donated, so we need sort it.*/
  list_sort(&sema->waiters, priority_less, NULL);
  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();
}

通過所有priority-donate-*

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章