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-*

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值