分类: LINUX
2011-11-25 21:28:49
锁大家接触的应该很早吧,家里经常使用锁来锁门。这里主要讲一下自旋锁。也许你会问,各种锁之间有什么区别?他们的主要区别是在申请锁失败后的表现形式,有的会不停的进行检查锁是否释放,而有的则去睡觉。我们下来讲到的自旋锁就属于前一种表现。
自旋锁的引入:自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量用于中断处理等。对于单处理器我们可以使用关中断来防止中断处理程序的并发执行。且自旋锁只能用于短时间的锁定,用于中断上下文。为什么不可以长时间拥有?前面已经说了,自旋锁在等待锁重新可用期间是在自旋,这对处理器来说是非常浪费时间且效率低下的。
自旋锁定义如下:
比较旧的内核:
typedef struct {
volatile unsigned int lock;
}spinlock_t;
3.0.4内核定义:
64 typedef struct spinlock {
65 union {
66 struct raw_spinlock rlock;
67
68 #ifdef CONFIG_DEBUG_LOCK_ALLOC
69 # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
70 struct {
71 u8 __padding[LOCK_PADSIZE];
72 struct lockdep_map dep_map;
73 };
74 #endif
75 };
76 } spinlock_t;
在新版源码里自旋锁共用了一段共享空间,里面定义了两个结构体, struct raw_spinlock的具体定义如下:
20 typedef struct raw_spinlock {
21 arch_spinlock_t raw_lock;
22 #ifdef CONFIG_GENERIC_LOCKBREAK
23 unsigned int break_lock;
24 #endif
25 #ifdef CONFIG_DEBUG_SPINLOCK
26 unsigned int magic, owner_cpu;
27 void *owner;
28 #endif
29 #ifdef CONFIG_DEBUG_LOCK_ALLOC
30 struct lockdep_map dep_map;
31 #endif
32 } raw_spinlock_t;
里面定义了一系列的锁。上面的 raw_spinlock_t类型其实就是 spinlock_t类型。
自旋锁的的基本使用如下:
DEFINE_SPINLOCK(x);
spin_lock(x);
/临界区*********/
spin_unlock(x);
自旋锁的初始化函数spin_lock_init(_lock) ,自旋锁的加锁和解锁函数定义如下:
283 static inline void spin_lock(spinlock_t *lock)
284 {
285 raw_spin_lock(&lock->rlock);
286 }
323 static inline void spin_unlock(spinlock_t *lock)
324 {
325 raw_spin_unlock(&lock->rlock);
326 }
由于在内核中大量地使用了spinlock,有大量的临界区存在,因此它们将严重地影响着系统的实时性。 (自旋) 因此以后使用互斥体(mutex)来代替自旋锁。
Spinlock失效抢占的目的是避免死锁。Spinlock如果可抢占了,一个spinlock的竞争者将可能抢占该spinlock的保持者来 运行,但是由于得不到spinlock将自旋在那里,如果竞争者的优先级高于保持者的优先级,将形成一种死锁的局面,因为保持者无法得到运行而永远不能释 放spinlock,而竞争者由于不能得到一个不可能释放的spinlock而永远自旋在那里。
由于中断处理函数也可以使用spinlock,如果它使用的spinlock已经被一个进程保持,中断处理函数将无法继续进行,从而形成死锁,这样 的spinlock在使用时应当中断失效来避免这种死锁的情况发生。标准linux内核就是这么做的,中断线程化之后,中断失效就没有必要,因为遇到这种 状况后,中断线程将挂在等待队列上并放弃CPU让别的线程或进程来运行。
等待队列就是解决这种死锁僵局的方法,每个spinlock都有一个等待队列,该等待队列是按进程或线程的优先级排队的。如果一个进程或线程竞争的spinlock 已经被另一个线程保持,它将把自己挂在该spinlock的优先级化的等待队列上,然后发生调度把CPU让给别的进程或线程。