内核同步方法之自旋锁
2008-12-19 11:52
218 查看
linux内核中最常见的锁是自旋锁(spin lock)。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被争用的自旋锁,那么该线程就会一直进行忙循环等待锁重新可用。要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。
一个被正用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理器时间),这种行为是自旋锁的特点。自旋锁不应该被长时间持有。持有自旋锁的时间最好小于完成两次上下文切换的的时间。
自旋锁的实现和体系结构密切相关,代码往往使用汇编实现。
因为自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只能有一个线程位于临界区内,这就为多处理器提供了防止并发访问所需要的包含机制。在单处理器机器上,编译的时候并不会加入自旋锁。
自旋锁不能递归!
自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它会导致睡眠)。在中断处理程序中使用自旋锁时,一定要在获取锁之前,首先禁本地中断(在当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。注意,需要关闭的只是当前处理器上的中断,因为如果中断发生在不同的处理器上,即使中断处理程序在同一锁上的自旋,也不会妨碍锁的持有者最终释放锁。
下面是相关文件如下:
/*
* include/linux/spinlock.h - generic spinlock/rwlock declarations
*
* here's the role of the various spinlock/rwlock related include files:
*
* on SMP builds:
*
* asm/spinlock_types.h: contains the raw_spinlock_t/raw_rwlock_t and the
* initializers
*
* linux/spinlock_types.h:
* defines the generic type and initializers
*
* asm/spinlock.h: contains the __raw_spin_*()/etc. lowlevel
* implementations, mostly inline assembly code
*
* (also included on UP-debug builds:)
*
* linux/spinlock_api_smp.h:
* contains the prototypes for the _spin_*() APIs.
*
* linux/spinlock.h: builds the final spin_*() APIs.
*
* on UP builds:
*
* linux/spinlock_type_up.h:
* contains the generic, simplified UP spinlock type.
* (which is an empty structure on non-debug builds)
*
* linux/spinlock_types.h:
* defines the generic type and initializers
*
* linux/spinlock_up.h:
* contains the __raw_spin_*()/etc. version of UP
* builds. (which are NOPs on non-debug, non-preempt
* builds)
*
* (included on UP-non-debug builds:)
*
* linux/spinlock_api_up.h:
* builds the _spin_*() APIs.
*
* linux/spinlock.h: builds the final spin_*() APIs.
*/
内核提供的禁止中断同时请求锁的接口:
在<Spinlock.h(include/linux)>中
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
#define spin_lock_irqsave(lock, flags) flags = _spin_lock_irqsave(lock)
#define read_lock_irqsave(lock, flags) flags = _read_lock_irqsave(lock)
#define write_lock_irqsave(lock, flags) flags = _write_lock_irqsave(lock)
#ifdef CONFIG_DEBUG_LOCK_ALLOC
#define spin_lock_irqsave_nested(lock, flags, subclass) /
flags = _spin_lock_irqsave_nested(lock, subclass)
#else
#define spin_lock_irqsave_nested(lock, flags, subclass) /
flags = _spin_lock_irqsave(lock)
#endif
#else
#define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(spinlock_t * lock)(lock, flags)
#define read_lock_irqsave(lock, flags) _read_lock_irqsave(lock, flags)
#define write_lock_irqsave(lock, flags) _write_lock_irqsave(lock, flags)
#define spin_lock_irqsave_nested(lock, flags, subclass) /
spin_lock_irqsave(lock, flags)
#endif
#define spin_unlock_irqrestore(lock, flags) /
_spin_unlock_irqrestore(lock, flags)
函数spin_lock_irqsave()保存中断的当然状态,并禁止本地中断,然后再去获取指定的锁。spin_lock_irqsave()对指定的锁解锁,然后让中断恢复到加锁前的状态。所以即使中断最初是被禁止的,你的代码也不会错误地激活它们,相反,会继续让它们禁止。flag变量应该由数值传递,因为锁函数有些是通过宏的方式实现的。
加锁和解锁分别可以禁止和允许内核抢占。
如果能确定中断在加锁前是激活的,就不需要在解锁后恢复中断之前的状态了。这时可以使用spin_lock_irq()和spin_unlock_irq()。
#define spin_lock_irq(lock) _spin_lock_irq(lock)
#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
!defined(CONFIG_SMP)
# define read_unlock_irq(lock) _read_unlock_irq(lock)
#else
# define spin_unlock_irq(lock) /
do { /
__raw_spin_unlock(&(lock)->raw_lock); /
__release(lock); /
local_irq_enable(); /
} while (0)
由于内核庞大而复杂,所以在内核的执行路线上,很难搞清楚中断在当前调用点上是否处于激活状态,故一般不提倡使用spin_lock_irq()方法。
针对自旋锁的操作:
spin_lock_init()用来初始化动态创建的自旋锁。
#ifdef CONFIG_DEBUG_SPINLOCK
extern void __spin_lock_init(spinlock_t *lock, const char *name,
struct lock_class_key *key);
# define spin_lock_init(lock) /
do { /
static struct lock_class_key __key; /
/
__spin_lock_init((lock), #lock, &__key); /
} while (0)
#else
# define spin_lock_init(lock) /
do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0)
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
extern void __rwlock_init(rwlock_t *lock, const char *name,
struct lock_class_key *key);
# define rwlock_init(lock) /
do { /
static struct lock_class_key __key; /
/
__rwlock_init((lock), #lock, &__key); /
} while (0)
#else
# define rwlock_init(lock) /
do { *(lock) = RW_LOCK_UNLOCKED; } while (0)
#endif
#define spin_is_locked(lock) __raw_spin_is_locked(&(lock)->raw_lock)
#define spin_trylock(lock) __cond_lock(lock, _spin_trylock(lock))
spin_trylock()试图获得某个特定的自旋锁,如果该锁已经被争用,那么立刻返回非0值,而不会自旋等待锁被释放;如果获得这个自旋锁,返回0。
spin_is_locked()用于检查特定的锁当前是否已被占用,如被占用返回非0,否则返回0。
#define spin_lock(lock) _spin_lock(lock)
/*
* We inline the unlock functions in the nondebug case:
*/
#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
!defined(CONFIG_SMP)
# define spin_unlock(lock) _spin_unlock(lock)
#else
# define spin_unlock(lock) /
do {__raw_spin_unlock(&(lock)->raw_lock); __release(lock); } while (0)
自旋锁和下半部
函数spin_lock_bh()用于获取指定锁,同时它会禁止所有下半部的执行。spin_unlock_bh()函数执行相反的操作。
#define spin_lock_bh(lock) _spin_lock_bh(lock)
#define spin_unlock_bh(lock) _spin_unlock_bh(lock)
void __lockfunc _spin_lock_bh(spinlock_t *lock)
{
local_bh_disable();
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
_raw_spin_lock(lock);
}
void __lockfunc _spin_unlock_bh(spinlock_t *lock)
{
spin_release(&lock->dep_map, 1, _RET_IP_);
_raw_spin_unlock(lock);
preempt_enable_no_resched();
local_bh_enable_ip((unsigned long)__builtin_return_address(0));
}
由于下半部可以抢占进程上下文中的代码,所以当下半部和进程上下文共享数据时,必须对进程上下文中的共享数据进行包含,所以需要加锁的同时还要禁止下半部执行。同样,由于中断处理程序可以抢占下半部,所以如果中断处理程序和下半部共享数据,那么就必须在获取恰当的锁的同时还有禁止中断。
相关文章推荐
- 9.2 内核同步方法_自旋锁
- 内核同步方法-自旋锁
- 9.3 内核同步方法_读-写自旋锁
- [转载] 《重要》内核下各种同步处理方法(自旋锁、信号灯、互斥体…)
- 内核下各种同步处理方法(自旋锁、信号灯、互斥体…)
- 内核同步方法之读写自旋锁
- 内核同步方法之自旋锁
- 内核同步之自旋锁和信号量
- 内核同步方法
- linux 内核同步机制-自旋锁与信号量及其区别
- Linux 同步方法剖析--内核原子,自旋锁和互斥锁
- 9.9 内核同步方法_禁止抢占
- 内核同步机制——自旋锁
- linux-android 中内核同步方法
- 内核同步方法二
- linux 内核同步机制-自旋锁与信号量及其区别
- Linux 内核同步之自旋锁与信号量的异同【转】
- 《Linux内核设计与实现》读书笔记(十)- 内核同步方法
- Linux内核同步介绍及方法
- Linux 进程管理剖析: Linux 同步方法剖析 内核原子,自旋锁和互斥锁