代码改变世界

几句话总结内核中的锁

2015-07-06 12:28  tolimit  阅读(2640)  评论(0编辑  收藏  举报

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/

 

原子操作(atomic):

        通过在汇编操作码前缀加个lock(0xf0)来给add命令上锁, 这个锁可能是对cache上锁(数据在cache中)或者对总线上锁(数据在内存中, 不在cache中),让其他CPU无法访问这个数据,直到这条指令完成。
 
 

自旋锁(spinlock/spinlock_irqsave):

        spinlock: 获取时“忙等”,进入临界区后禁止抢占,“忙等”期间可抢占和中断。
        spinlock_irqsave: 获取时“忙等”,进入临界区后禁止中断和抢占,“忙等”期间可抢占和中断。
  spinlock_bh: 获取时“忙等”,进入临界区后禁止软中断,“忙等”期间可抢占和中断。并且注意,当spin_unlock_bh时,如果有softirq挂起,则会执行do_softirq将这些挂起的softirq执行.
  自旋锁分为临界区中关中断和开中断两种类型(临界区中都会禁止抢占)。在单核系统上,自旋锁只起到禁止和开启内核抢占的作用。
  实现方式: 使用head和tail, tail为原子变量, 当spin_lock()时, 获取tail并且tail++, 如果head == tail, 说明lock成功, 如果head != tail, 则一直循环等待tail等于head为止, 当spin_unlock()时, head++.
 
 

读写自旋锁(rwlock):

  读写锁中包含读锁(读数据锁)和写锁(写数据锁),一个读写锁同时只能有一个写者或多个读者,不能同时既有读者又有写者。与自旋锁一样获取时会“忙等”,“忙等”期间可抢占,读锁和写锁进入临界区后都禁止内核抢占。
  实现方式: 同样使用原子变量,变量>0,表示有多个读者,变量==0表示锁空闲,变量<0(只能等于-1)表示有写者。读者获取锁时,如果变量>0, 就获取锁,并且变量++,unlock时变量--。写者获取锁时,循环等待变量==0,获取到时变量--.
 

顺序自旋锁(seqlock):

        读写锁的变种,读者可以并发,写者只能串行写,写的优先级高于读。临界区中可同时拥有读者和写者,导致读者读数据前后都要检查顺序计数器的值,如果值为奇数则继续读取数据。
  实现方式: 使用一个原子的计数器,顺序计数器的值只加不减,写者写过程中,计数器为奇数,释放写锁后会偶数,读临界区不会禁止内核抢占,写会禁止内核抢占。
 
 

RCU(读-拷贝-更新):

        RCU允许多个读者和多个写者并发执行,并不使用锁。RCU只保护被动态分配内存并通过指针引用的数据结构。写者在访问时首先会拷贝一个副本,然后对副本进行修改,最后使用一个回调机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作。在临界区中禁止内核抢占。
 
 

信号量(semaphore):

        临界区忙时获取信号量的进程会被睡眠(挂起到一个等待队列中,状态为TASK_UNINTERRUPTIBLE),因此在中断上下文中不能使用内核信号量。同时只能有一个路径进入临界区的信号量称为互斥锁(mutex)。在临界区的进程可以睡眠和被调度.
 
 

读写信号量:

        与读写自旋锁类似,只是临界区忙时获取锁不是“忙等”而是睡眠(挂起)。