大多数锁定通过称为"自旋锁"的机制实现. 和信号量不同, 自旋锁可以在不能休眠的代码中使用, 比如: 中断处理例程. 在正确使用的情况下, 自旋锁通常可以提供比信号量更高的性能, 但它也有一组不同的使用限制.
1. 概念:
自旋锁在概念上非常简单, 一个自旋锁就是一个互斥设备, 它只有两个值: "锁定"和"解锁". 它通常实现为某个整数值中的单个位.
- 希望获得某特定锁的代码, 测试相关的位. (原子操作)
- 如果锁可用, 则"锁定"位被设置, 而代码继续进入临界区.
- 相反, 如果锁被其他人获得, 则代码进入忙循环并重复检查这个锁, 直到该锁可用为止.
2. 自旋锁基本API:
- 头文件: <linux/spinlock.h>
- 数据类型: spinlock_t
- 初始化, 以下两种方法:
- 静态: spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
- 动态: void spin_lock_init(spinlock_t *lock);
- 获得锁: void spin_lock(spinlock_t *lock);
- 释放锁: void spin_unlock(spinlock_t *lock);
3. 自旋锁其他API:
锁定一个自旋锁的函数实际有4个:
/* 基本函数 */
void spin_lock(spinlock_t *lock);
/* 会在获得自旋锁之前禁止中断, 而先前的中断状态保存在flags中 */
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
/* 同上, 但无跟踪标志 */
void spin_lock_irq(spinlock_t *lock);
/* 在获得锁之前禁止软件中断 */
void spin_lock_bh(spinlock_t *lock);
对应的, 也有4个unlock函数:
void spin_unlock(spinlock_t *lock);
/* 这里面的flags必须和传递给spinlock_lock_irqsave的是同一个变量,
* 还必须在同一函数中调用, 否则代码可能在某些架构上出现问题 */
void spin_unlock_irqstore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
同时, 还有2个非阻塞的自旋锁操作:
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
对禁止中断的情况, 没有对应的"try"版本.
4. 后续:
内核还提供了读取者/写入者形式的自旋锁, 这种锁与早先介绍的读取者/写入者信号量很相似. 因为不再赘述. 同时还有锁陷阱, 这些都会在以后的研究文章中出现.