1.3.1 spin lock
这种锁定机制在执行时间一次只能由一个线程持有锁。如果一个线程已持有了锁,另一个执行线程想要获取锁时,就只能循环等待直到前一个线程将锁释放,也即在此期间处理器不做别的处理,一直在循环测试锁的状态。因此spin lock只适用于多处理器系统运行环境,而且通常用于预计锁可以在很短时间内就能获取的情况下。使用spin lock时,要求持有锁的线程不能休眠,否则会造成别的线程因不能获取锁而发生系统死锁的情况。
spin lock又称为忙等锁定机制,它适用于当一段critical section代码部分非常短,执行起来很快的合。这时如果我们采用的不是spin lock锁定机制,而是采用另一种方式,比如,当CPU测试到锁被别的活动持有,就用调度器将当前活动换出CPU,调度别的进程来执行;锁有效后再把该活动调度进来执行,其中调度进程进出CPU所花费的时间可能比处理器忙而等待所用的时间更长,效率更低。因此,对很小的critical section程序段的保护采用spin locks更好。
在Linux内核中,spin lock由spinlock_t类型的变量实现,它由一个整数的锁变量组成,为了使用spin lock,首先要创建和初始化spinlock_t数据结构,使用spin lock的步骤为:
#include < linux/spinlock.h> spinlock_t my_spinlock=SPIN_LOCK_UNLOCKED; //初始化锁变量,或使用函数,初始化锁变量 spin_lock_init( &my_spinlock) //将锁初始化为未锁定状态
接下来你可以用内核提供的一系列函数在需要的时候测试、获取或释放锁。获取锁的地方标志着critical section部分的开始,释放锁处标志着critical section部分的结束,这时别的活动又可以来获取锁,操作共享的数据结构。
1.获取锁
(1)spin_lock(spinlock_t *my_spinlock)
试图获取锁my_spinlock,如锁已被别的进程持有,必须等待并不断测试锁的状态直到锁释放。锁被释放后可立刻获取。
(2)spin_lock_irqsave(spinlock_t *my_spinlock, unsigned long flags)
与spin_lock函数的功能类似,它自动屏蔽中断,将CPU的当前状态寄存器值保存到变量flags中。
(3)spin_lock_irq(spinlock_t *my_spinlock)
与spin_lock_irqsave类似,但不保存CPU状态寄存器的值,它假定中断已经屏蔽了。
(4)spin_lock_bh(spinlock_t *my_spinlock)
获取锁,同时阻止bottom half的运行。
2.释放锁
(1)spin_unlock(spinlock_t *my_spinlock)
释放一个获取的锁。
(2)spin_unlock_irqrestore(spinlock_t *my_spinlock)
释放一个spinlock的锁,并置中断允许。
(3)spin_unlock_irq(spinlock_t *my_spinlock)
释放锁,允许中断。
(4)spin_unlock_bh(spinlock_t *my_spinlock)
释放锁,并允许立即处理bottom half。
以上的函数可以用于保护critical section代码的执行,避免多个活动同时操作共享数据结构带来不可预期的错误。比如,在图1-2中所示的操作可以用以下方式来保护活动A和活动B都要操作的链表。
spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED; // 活动A spin_lock(&my_spinlock); skb_a->next = queue->next; queue->next = skb_a; spin_unlock(&my_spinlock); … //活动B spin_lock(&my_spinlock); skb_b->next = queue->next; queue->next = skb_b; spin_unlock(&my_spinlock);
使用了锁定机制来保护链表后,活动A与活动B向链表中插入数据的操作就不会再出错了。