自旋锁概念上简单.一个自旋锁是一个互斥设备,只能有2个值:"上锁"和"解锁".它常常实现为一个整数值中的一个单个位.想获取一个特殊锁的代码测试相关的位.如果锁是可用的,这个"上锁"位被置位并且代码继续进入临界区.相反,如果这个锁已经被别人获得,代码进入一个紧凑的循环中反复检查这个锁,直到它变为可用.这个循环就是自旋锁的"自旋"部分.
自旋锁API简介
自旋锁原语要求的包含文件是.一个实际的锁有类型spinlock_t.象任何其他数据结构,一个 自旋锁必须初始化.这个初始化可以在编译时完成,如下:
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
或者在运行时使用:
void spin_lock_init(spinlock_t *lock);
在进入一个临界区前,你的代码必须获得需要的lock ,用:
void spin_lock(spinlock_t *lock);
注意所有的自旋锁等待是,由于它们的特性,不可中断的.一旦你调用spin_lock,你将自旋直到锁变为可用.
为释放一个你已获得的锁,传递它给:
void spin_unlock(spinlock_t *lock);
其他参考内容:
自旋锁函数
我们已经看到2个函数, spin_lock和spin_unlock,可以操作自旋锁.有其他几个函数,然而,有类似的名子和用途.我们现在会展示全套.这个讨论将带我们到一个我们无法在几章内适当涵盖的地方;自旋锁API的完整理解需要对中断处理和相关概念的理解.
实际上有4个函数可以加锁一个自旋锁:
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
void spin_lock_bh(spinlock_t *lock)
我们已经看到自旋锁如何工作. spin_loc_irqsave禁止中断(只在本地处理器)在获得自旋锁之前;之前的中断状态保存在flags里.如果你绝对确定在你的处理器上没有禁止中断的(或者,换句话说,你确信你应当在你释放你的自旋锁时打开中断),你可以使用spin_lock_irq代替,并且不必保持跟踪flags.最后, spin_lock_bh在获取锁之前禁止软件中断,但是硬件中断留作打开的.
如果你有一个可能被在(硬件或软件)中断上下文运行的代码获得的自旋锁,你必须使用一种spin_lock形式来禁止中断.其他做法可能死锁系统,迟早.如果你不在硬件中断处理里存取你的锁,但是你通过软件中断(例如,在一个tasklet运行的代码,在第7章涉及的主题),你可以使用spin_lock_bh来安全地避免死锁,而仍然允许硬件中断被服务.
也有4个方法来释放一个自旋锁;你用的那个必须对应你用来获取锁的函数.
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
每个spin_unlock变体恢复由对应的spin_lock函数锁做的工作.传递给spin_unlock_irqrestore的flags参数必须是传递给spin_lock_irqsave的同一个变量.你必须也调用spin_lock_irqsave和spin_unlock_irqrestore在同一个函数里.否则,你的代码可能破坏某些体系.
还有一套非阻塞的自旋锁操作:
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
这些函数成功时返回非零(获得了锁),否则0.没有"try"版本来禁止中断.
读者/写者自旋锁
内核提供了一个自旋锁的读者/写者形式,直接模仿我们在本章前面见到的读者/写者旗标.这些锁允许任何数目的读者同时进入临界区,但是写者必须是排他的存取.读者写者锁有一个类型rwlock_t,在中定义.它们可以以2种方式被声明和被初始化:
rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* Static way */
rwlock_t my_rwlock;
rwlock_init(&my_rwlock); /* Dynamic way */
可用函数的列表现在应当看来相当类似.对于读者,下列函数是可用的:
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
有趣地,没有read_trylock.对于写存取的函数是类似的:
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
int write_trylock(rwlock_t *lock);
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
读者/写者锁能够饿坏读者,就像rwsem一样.