Three easy pieces 第二部分(2)

1.如何评价构造的lock?

    (1)功能性。就是你这个lock到底能不能有效果。

    (2)公平性。你不能让某个thead永远获取不到lock

    (3)效率。对于只有一个thread的情况不能因为锁代码导致效率变低。分别对于单核多核的情况,锁的获取和释放的效率如何

2.如果构造lock?

     (1)停止中断

     (2)Test-and-Set。硬件提供原子性的操作,从而可以构成一个spin-lock的锁,原子性操作步骤按照如下的C语言来表示。

        int TestAndSet(int *old_ptr, int new) {
          int old = *old_ptr; // fetch old value at old_ptr3 
          *old_ptr = new; // store ’new’ into old_ptr4 
          return old; // return the old value5 }   
typedef struct __lock_t {
int flag;
} lock_t;

void init(lock_t *lock) {
// 0 indicates that lock is available, 1 that it is held
lock->flag = 0;
}

void lock(lock_t *lock) {
while (TestAndSet(&lock->flag, 1) == 1)
; // spin-wait (do nothing)
}

void unlock(lock_t *lock) {
lock->flag = 0;
}

从如上的角度分析这个锁,首先是可以确保功能是对的。不过无法保证公平性。在单核的机器上因为大量的无用的循环,效率会很低,不过在多核的机器上效率会有所提高。

(3)Compare-and-swap

int CompareAndSwap(int *ptr, int expected, int new) {
   int actual = *ptr;
   if (actual == expected)
   *ptr = new;
   return actual;
}

(4)load-linked and store-conditional

int LoadLinked(int *ptr) {
  return *ptr;
}
int StoreConditional(int *ptr, int value) {
  if (no one has updated *ptr since the LoadLinked to this address) {
       *ptr = value;
       return 1; // success!
} else {
   return 0; // failed to update
 }
}
void lock(lock_t *lock) {
   while (1) {
   while (LoadLinked(&lock->flag) == 1)
       ; // spin until it’s zero
   if (StoreConditional(&lock->flag, 1) == 1)
      return; // if set-it-to-1 was a success: all done
              // otherwise: try it all over again
   }
}

void unlock(lock_t *lock) {
 lock->flag = 0;
}
void lock(lock_t *lock) {
    while (LoadLinked(&lock->flag)||!StoreConditional(&lock->flag, 1))
      ; // spin
}

(5)fetch-and-add

int FetchAndAdd(int *ptr) {
  int old = *ptr;
  *ptr = old + 1;
  return old;
}
typedef struct __lock_t {
  int ticket;
  int turn;
} lock_t;

void lock_init(lock_t *lock) {
  lock->ticket = 0;
  lock->turn = 0;
}

void lock(lock_t *lock) {
 int myturn = FetchAndAdd(&lock->ticket);
 while (lock->turn != myturn)
 ; // spin
}

void unlock(lock_t *lock) {
 lock->turn = lock->turn + 1;
}

这种锁和前面提到的几种锁相比会更加公平,因为这种加锁方式相当于给每个thread一个入场券,在前面的thread执行完成后,就肯定会可以得到lock。

3.如何避免空轮转

    上面的硬件给出的原子性指令,在用于构造lock 的时候都会产生许多的空转,为了避免这种情况,该是操作系统介入的时候了。可以有如下的方式。

     (1)Just yield,baby!

           就是说在获取锁的时候不会去死循环,使用系统调用yield(),这样调度器就不会去调度这个thread。这种方式相对于空循环会节省浪费,但是如果线程多,那么每个线程还是需要去执行一次yield(),并且也不能解决公平性问题。

       (2)使用等待队列代替简单的调入调出           

typedef struct __lock_t {
  int flag;
  int guard;
  queue_t *q;
} lock_t;

void lock_init(lock_t *m) {
   m->flag = 0;
   m->guard = 0;
   queue_init(m->q);
 }

void lock(lock_t *m) {
 while (TestAndSet(&m->guard, 1) == 1)
    ; //acquire guard lock by spinning
 if (m->flag == 0) {
    m->flag = 1; // lock is acquired
    m->guard = 0;
 } else {
    queue_add(m->q, gettid());
    m->guard = 0;
    park();
 }
}

void unlock(lock_t *m) {
     while (TestAndSet(&m->guard, 1) == 1)
         ; //acquire guard lock by spinning
 if (queue_empty(m->q))
     m->flag = 0; // let go of lock; no one wants it
 else
     unpark(queue_remove(m->q)); // hold lock (for next thread!)
 m->guard = 0;
}
park()系统调用会将thead sleep,unpark(theadId)则是将对应的thread置为ready状态。这2个系统调用是Solaris提供的系统调用。但是上述的方式有个问题,那就是如果一个thread在park()调用前被切换为另外一个thread运行,后一个thread运行完以后加入unpark第一个thread,就会出现wait/wake up 竞态条件,所以Solaris提供了一个改进的系统调用setPark()。代码改为如下:
 queue_add(m->q, gettid());
 setpark(); // new code
 m->guard = 0;

这样如果出现上面的情况,setpark不会让thread 休眠,而是立刻返回。

(3)linux的futex方式

void mutex_lock (int *mutex) {
  int v;
  /* Bit 31 was clear, we got the mutex (this is the fastpath) */
  if (atomic_bit_test_set (mutex, 31) == 0)
    return;
  atomic_increment (mutex);
  while (1) {
    if (atomic_bit_test_set (mutex, 31) == 0) {
       atomic_decrement (mutex);
       return;
 }
 /* We have to wait now. First make sure the futex value
 we are monitoring is truly negative (i.e. locked). */
 v = *mutex;
 if (v >= 0)
 continue;
 futex_wait (mutex, v);
 }
}

 void mutex_unlock (int *mutex) {
 /* Adding 0x80000000 to the counter results in 0 if and only if
 there are not other interested threads */
 if (atomic_add_zero (mutex, 0x80000000))
    return;

 /* There are other threads waiting for this mutex,
 wake one of them up. */
 futex_wake (mutex);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《操作系统三部曲:易懂的操作系统原理》是一本经典的操作系统教材,由Remzi H. Arpaci-Dusseau夫妇撰写。这本书旨在以简单易懂的方式阐述操作系统原理。它被广泛用作操作系统课程的教材,并受到学生和教师的赞赏。 《操作系统三部曲:易懂的操作系统原理》通过深入讲解操作系统的关键概念和原理,帮助读者理解操作系统的内部运作方式。书中内容丰富,包括处理器调度、内存管理、文件系统、存储器层次结构等多个重要主题。每个主题被简明扼要地阐述,使读者易于理解。 相比其他操作系统教材,《操作系统三部曲:易懂的操作系统原理》的独特之处在于其注重概念和实践的结合。通过大量的案例研究和实验,读者可以巩固所学知识,并深入理解实际系统的工作原理。 这本教材还介绍了一些最新的操作系统技术,如虚拟化和云计算。这些新颖的话题增强了读者对现代操作系统的认识,并使他们能够跟上技术的发展趋势。 《操作系统三部曲:易懂的操作系统原理》是一本深入浅出的操作系统教材,适合初学者和有经验的读者阅读。它详尽而精确地介绍了操作系统的各个方面,给读者提供了对该领域的全面了解。无论是作为教材还是作为参考书,这本书都是学习操作系统的不可或缺的资源。 ### 回答2: 《Operating Systems: Three Easy Pieces》是一本关于操作系统的教材。它由Remzi H. Arpaci-Dusseau夫妇合著,并于2014年发布。这本教材旨在为学生和对计算机科学感兴趣的读者提供关于操作系统的详细介绍和理解。 这本书的主要目标是帮助读者理解操作系统的设计原理和实验技术。它通过探讨操作系统的各个组成部分,如处理器调度、内存管理、文件系统、网络通信等,来解释操作系统的工作原理。书中使用了许多实例和示意图来解释复杂的概念,以确保读者可以轻松理解并应用所学知识。 《Operating Systems: Three Easy Pieces》涵盖了操作系统的基本概念和高级主题。它从基本的进程和线程管理开始,介绍了虚拟内存和文件系统等更高级的概念。这本书还讨论了操作系统的并发性和死锁等问题,并提供了解决这些问题的方法和技术。 这本书还特别关注了操作系统的实验。它提供了许多实验项目,供读者亲自动手实践操作系统的设计和实现。这些实验帮助读者巩固所学理论,并提供了实际的编程经验。 总的来说,《Operating Systems: Three Easy Pieces》是一本系统而全面的操作系统教材。它通过简单易懂的语言和丰富的实例,帮助读者深入理解操作系统的原理和实践。无论是学生还是对计算机科学感兴趣的读者,都能够从中获得宝贵的知识和技能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值