spin lock与互斥锁的区别

  关于这两种锁的区别,有的资料只是介绍了机制上的区别,没有介绍使用的场景,比如为什么有时候一定要使用spin lock呢?

  1、先看自旋锁的特点:如果锁已经被别的执行单元占用,那么加锁操作会一直在那边死等。

  2、如果保持锁的时间非常短,那就选自旋锁,主要是不让执行单元睡眠,这样效率上高。如果时间长就选择信号量和读写信号量。

  什么情况下必须使用spin lock呢?

  答案是:内核可抢占或者SMP的情况下,在单cpu且不可抢占的内核下,不需要自旋锁。如果不用会发生什么情况呢?会发生死锁。一个占着没释放,一个在死等。

  spin lock相关的实现有好几个,使用哪个主要取决于临界区资源共享的实体。

  1、 对于单cpu内核是抢占的,如果资源共享发生在不同进程之间那么用spin lock就行;如果中断也要访问临界区,那么就要用spin lock irq

  2、对于多cpu一样,不管抢占不抢占,要用spin lock,如果中断也要访问临界区,那么用spin lock irq

  所以,有中断访问就用spin lcok irq,没有中断参与就用spin lock,或者有下半部访问,用spin lock bh

  更多的情况参考这里:

  http://m.blog.chinaunix.net/uid-9620812-id-1643085.html,一下是链接内容的部分拷贝。

获得自旋锁和释放自旋锁有好几个版本,因此让读者知道在什么样的情况下使用什么版本的获得和释放锁的宏是非常必要的。

  如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。
  当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。
  如果被保护的共享资源只在进程上下文和tasklet或timer上下文访问,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklet和timer是用软中断实现的。
  如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。
  timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。
  如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
 如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
  如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
  如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
  而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对共享资源的访问就可以了。
  因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
  在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些。
  因为它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。
  当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irq和spin_unlock_irq最好。

  spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问。
       
       我个人觉得:内核控制路径(进程的内核态访问、硬中断、下半部)互斥问题的关键是弄清临界区的使用性质,就是要看那些路径要访问共享资源:
1 看多CPU是否访问共享资源:若是,要应用spin_lock 或 semaphore, semaphore可以发生进程上下文切换,在多CPU的情况下,也依靠spin_lock实现多CPU的互斥。
      当然,如果能够确定临界区在中断上下文中肯定要用spin_lock。
      a)   看硬中断是否访问临界区域:若是,要用spin_lock_irq()/spin_unlock_irq,要保存标志寄存器就用spin_lock_irqsave()/spin_unlock_irqrestrore()。
      b)   若临界区可能有下半部访问(timer_list 、tasklet也属于这里,因为他们依靠下半部实现的),若是,则要spin_lock_bh()/spin_unlock_bh
2 若不受多CPU的影响,则再看硬中断、下半部的影响,相应的使用关闭本地硬中断或BH
       local_irq_disable/local_irq_enable
      local_bh_disable/local_bh_enable
 
      总之:就是多CPU、内核抢占、硬中断、小半部的问题,spin_lock处理多CPU的互斥,本地硬中断和本地下半部的问题由local_irq_disable local_bh_disable完成。

猜你喜欢

转载自www.cnblogs.com/keenxu/p/11518530.html