对于互斥,信号灯(semaphore)是一个有用的工具,但是它们不是内核提供的唯一这样的工具,相反,大部分加锁是由一种称为自旋锁的机制来实现的。不想信号灯,自旋锁可用在不能睡眠的代码中,例如中断处理。当正确使用了,通常自旋锁提供了比信号灯更高的性能。
一个自旋锁是一个互斥设备,只能有两个值:“上锁”和“解锁”。它常常实现为一个整数值中的一个单个位。想获取一个特殊锁的代码测试相关的位。如果锁是可用的,这个“上锁”位被置位并且代码继续进入临界区。相反,如果这个锁已经被别人获得,代码进入一个紧凑的循环中反复检查这个锁,直到它变得可用,这个循环就是自旋锁的“自旋”部分。
这个“测试并置位”操作必须以原子方式进行。
自旋锁原语要求的包含文件<linux/spinlock.h>,一个实际的锁有类型spinlock_t,像任何其他数据结构,一个自旋锁必须初始化,这个初始化可以在编译时完成,如下:
spinlock_t my_lock = SPIN_LOCK_UNLOCK;
或者在运行时使用:
void spin_lock_init(spinlock_t *lock);
在进入一个临界区时,代码必须获得需要的lock,用:
void spin_lock(spinlock_t *lock);
注意所有的自旋锁等待的是不可中断的,一旦你调用spin_lock,你将自旋直到锁变得可用。很容易出现死锁的情况。
为释放一个你已获得的锁,传递它给:
spin_unlock(spinlock_t *lock);
想象你的驱动请求一个自旋锁并且在它的临界区里做它的事情,在中间某处,你的驱动失去了处理器,或者它已调用了一个函数(copy_from _user,假设)使进程进入了睡眠,或者内核抢占的作用,而使另一个更高优先级的进程将你的代码推到一边。你的代码现在持有一个锁,在可见的未来将不会释放这个锁,如果别的某个线程想获得同一个锁,在最好的情况下,它会等待(在处理器中自旋)很长时间;最坏的情况下,系统可能出现死锁。
因此,应用自旋锁的核心规则是任何代码在持有自旋锁时时原子性的,它不能睡眠;事实上,它不能因为任何原因放弃处理器,除了服务中断(并且有时即便此时也不行)。
如果中断处理和起初获得锁的代码在同一个处理器上会发生什么?当中断处理在自旋,非中断代码不能运行来释放锁,这个处理器将永远自旋。因此,持有自旋锁时,禁止中断(只在本地CPU),有各种自旋锁函数会为你禁止中断。
最后,自旋锁必须一致是尽可能短时间的持有。
这里还有关于其他自旋锁函数的介绍:
http://www.360doc.com/content/10/1008/16/1317564_59357570.shtml