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向链表中插入数据的操作就不会再出错了。