内核同步方法之自旋锁

来源:互联网 发布:数组删除字符串函数 编辑:程序博客网 时间:2024/05/02 06:13
 

       linux内核中最常见的锁是自旋锁(spin lock)。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被争用的自旋锁,那么该线程就会一直进行忙循环等待锁重新可用。要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。

      一个被正用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理器时间),这种行为是自旋锁的特点。自旋锁不应该被长时间持有。持有自旋锁的时间最好小于完成两次上下文切换的的时间。

     自旋锁的实现和体系结构密切相关,代码往往使用汇编实现。

     因为自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只能有一个线程位于临界区内,这就为多处理器提供了防止并发访问所需要的包含机制。在单处理器机器上,编译的时候并不会加入自旋锁。

     自旋锁不能递归!

自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它会导致睡眠)。在中断处理程序中使用自旋锁时,一定要在获取锁之前,首先禁本地中断(在当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。注意,需要关闭的只是当前处理器上的中断,因为如果中断发生在不同的处理器上,即使中断处理程序在同一锁上的自旋,也不会妨碍锁的持有者最终释放锁。

  下面是相关文件如下:

  1. /*
  2.  * include/linux/spinlock.h - generic spinlock/rwlock declarations
  3.  *
  4.  * here's the role of the various spinlock/rwlock related include files:
  5.  *
  6.  * on SMP builds:
  7.  *
  8.  *  asm/spinlock_types.h: contains the raw_spinlock_t/raw_rwlock_t and the
  9.  *                        initializers
  10.  *
  11.  *  linux/spinlock_types.h:
  12.  *                        defines the generic type and initializers
  13.  *
  14.  *  asm/spinlock.h:       contains the __raw_spin_*()/etc. lowlevel
  15.  *                        implementations, mostly inline assembly code
  16.  *
  17.  *   (also included on UP-debug builds:)
  18.  *
  19.  *  linux/spinlock_api_smp.h:
  20.  *                        contains the prototypes for the _spin_*() APIs.
  21.  *
  22.  *  linux/spinlock.h:     builds the final spin_*() APIs.
  23.  *
  24.  * on UP builds:
  25.  *
  26.  *  linux/spinlock_type_up.h:
  27.  *                        contains the generic, simplified UP spinlock type.
  28.  *                        (which is an empty structure on non-debug builds)
  29.  *
  30.  *  linux/spinlock_types.h:
  31.  *                        defines the generic type and initializers
  32.  *
  33.  *  linux/spinlock_up.h:
  34.  *                        contains the __raw_spin_*()/etc. version of UP
  35.  *                        builds. (which are NOPs on non-debug, non-preempt
  36.  *                        builds)
  37.  *
  38.  *   (included on UP-non-debug builds:)
  39.  *
  40.  *  linux/spinlock_api_up.h:
  41.  *                        builds the _spin_*() APIs.
  42.  *
  43.  *  linux/spinlock.h:     builds the final spin_*() APIs.
  44.  */

  内核提供的禁止中断同时请求锁的接口:

  1. 在<Spinlock.h(include/linux)>中
  2. #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
  3. #define spin_lock_irqsave(lock, flags)  flags = _spin_lock_irqsave(lock)
  4. #define read_lock_irqsave(lock, flags)  flags = _read_lock_irqsave(lock)
  5. #define write_lock_irqsave(lock, flags) flags = _write_lock_irqsave(lock)
  6. #ifdef CONFIG_DEBUG_LOCK_ALLOC
  7. #define spin_lock_irqsave_nested(lock, flags, subclass) /
  8.     flags = _spin_lock_irqsave_nested(lock, subclass)
  9. #else
  10. #define spin_lock_irqsave_nested(lock, flags, subclass) /
  11.     flags = _spin_lock_irqsave(lock)
  12. #endif
  13. #else
  14. #define spin_lock_irqsave(lock, flags)  _spin_lock_irqsave(spinlock_t * lock)(lock, flags)
  15. #define read_lock_irqsave(lock, flags)  _read_lock_irqsave(lock, flags)
  16. #define write_lock_irqsave(lock, flags) _write_lock_irqsave(lock, flags)
  17. #define spin_lock_irqsave_nested(lock, flags, subclass) /
  18.     spin_lock_irqsave(lock, flags)
  19. #endif
  1. #define spin_unlock_irqrestore(lock, flags) /
  2.                     _spin_unlock_irqrestore(lock, flags)

   函数spin_lock_irqsave()保存中断的当然状态,并禁止本地中断,然后再去获取指定的锁。spin_lock_irqsave()对指定的锁解锁,然后让中断恢复到加锁前的状态。所以即使中断最初是被禁止的,你的代码也不会错误地激活它们,相反,会继续让它们禁止。flag变量应该由数值传递,因为锁函数有些是通过宏的方式实现的。

   加锁和解锁分别可以禁止和允许内核抢占。

   如果能确定中断在加锁前是激活的,就不需要在解锁后恢复中断之前的状态了。这时可以使用spin_lock_irq()和spin_unlock_irq()。

  1. #define spin_lock_irq(lock)     _spin_lock_irq(lock)
  2. #if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
  3.     !defined(CONFIG_SMP)
  4. # define read_unlock_irq(lock)      _read_unlock_irq(lock)
  5. #else
  6. # define spin_unlock_irq(lock)          /
  7. do {                        /
  8.     __raw_spin_unlock(&(lock)->raw_lock);   /
  9.     __release(lock);            /
  10.     local_irq_enable();         /
  11. while (0)

    由于内核庞大而复杂,所以在内核的执行路线上,很难搞清楚中断在当前调用点上是否处于激活状态,故一般不提倡使用spin_lock_irq()方法。

 

    针对自旋锁的操作:

spin_lock_init()用来初始化动态创建的自旋锁。

  1. #ifdef CONFIG_DEBUG_SPINLOCK
  2.   extern void __spin_lock_init(spinlock_t *lock, const char *name,
  3.                    struct lock_class_key *key);
  4. # define spin_lock_init(lock)                   /
  5. do {                                /
  6.     static struct lock_class_key __key;         /
  7.                                 /
  8.     __spin_lock_init((lock), #lock, &__key);        /
  9. while (0)
  10. #else
  11. # define spin_lock_init(lock)                   /
  12.     do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0)
  13. #endif
  14. #ifdef CONFIG_DEBUG_SPINLOCK
  15.   extern void __rwlock_init(rwlock_t *lock, const char *name,
  16.                 struct lock_class_key *key);
  17. # define rwlock_init(lock)                  /
  18. do {                                /
  19.     static struct lock_class_key __key;         /
  20.                                 /
  21.     __rwlock_init((lock), #lock, &__key);           /
  22. while (0)
  23. #else
  24. # define rwlock_init(lock)                  /
  25.     do { *(lock) = RW_LOCK_UNLOCKED; } while (0)
  26. #endif
  1. #define spin_is_locked(lock)    __raw_spin_is_locked(&(lock)->raw_lock)
  1. #define spin_trylock(lock)      __cond_lock(lock, _spin_trylock(lock))

spin_trylock()试图获得某个特定的自旋锁,如果该锁已经被争用,那么立刻返回非0值,而不会自旋等待锁被释放;如果获得这个自旋锁,返回0。

spin_is_locked()用于检查特定的锁当前是否已被占用,如被占用返回非0,否则返回0。

  1. #define spin_lock(lock)         _spin_lock(lock)
  2. /*
  3.  * We inline the unlock functions in the nondebug case:
  4.  */
  5. #if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
  6.     !defined(CONFIG_SMP)
  7. # define spin_unlock(lock)      _spin_unlock(lock)
  8. #else
  9. # define spin_unlock(lock) /
  10.     do {__raw_spin_unlock(&(lock)->raw_lock); __release(lock); } while (0)

自旋锁和下半部

函数spin_lock_bh()用于获取指定锁,同时它会禁止所有下半部的执行。spin_unlock_bh()函数执行相反的操作。

  1. #define spin_lock_bh(lock)      _spin_lock_bh(lock)
  2. #define spin_unlock_bh(lock)        _spin_unlock_bh(lock)
  3. void __lockfunc _spin_lock_bh(spinlock_t *lock)
  4. {
  5.     local_bh_disable();
  6.     preempt_disable();
  7.     spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
  8.     _raw_spin_lock(lock);
  9. }
  10. void __lockfunc _spin_unlock_bh(spinlock_t *lock)
  11. {
  12.     spin_release(&lock->dep_map, 1, _RET_IP_);
  13.     _raw_spin_unlock(lock);
  14.     preempt_enable_no_resched();
  15.     local_bh_enable_ip((unsigned long)__builtin_return_address(0));
  16. }
  由于下半部可以抢占进程上下文中的代码,所以当下半部和进程上下文共享数据时,必须对进程上下文中的共享数据进行包含,所以需要加锁的同时还要禁止下半部执行。同样,由于中断处理程序可以抢占下半部,所以如果中断处理程序和下半部共享数据,那么就必须在获取恰当的锁的同时还有禁止中断。

 

原创粉丝点击