自旋锁最多只能被
个可执行线程持有
自旋锁不会引起
者睡眠
如果
个执行线程试图获得
个已经被持有
自旋锁
那么线程就会
直进行忙循环
直等待下去
在那里看是否该自旋锁
保持者已经释放了锁
"自旋"
词就是因此而得名
由于自旋锁使用者
般保持锁时间非常短
因此选择自旋而不是睡眠是非常必要
自旋锁
效率远高于互斥锁
信号量和读写信号量适合于保持时间较长
情况
它们会导致
者睡眠
因此只能在进程上下文使用(_trylock
变种能够在中断上下文使用);而自旋锁适合于保持时间非常短
情况
个被争用
自旋锁使得请求它
线程在等待重新可用时自旋
特别浪费处理时间
这是自旋锁
要害的处
所以自旋锁不应该被长时间持有
在实际应用中自旋锁代码只有几行
而持有自旋锁
时间也
般不会超过两次上下方切换
因线程
旦要进行切换
就至少花费切出切入两次
自旋锁
占用时间如果远远长于两次上下文切换
我们就可以让线程睡眠
这就失去了设计自旋锁
意义
如果被保护
共享资源只在进程上下文访问
使用信号量保护该共享资源非常合适
如果对共享资源
访问时间非常短
自旋锁也可以
但是如果被保护
共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断)
就必须使用自旋锁
自旋锁保持期间是抢占失效
而信号量和读写信号量保持期间是可以被抢占
自旋锁只有在内核可抢占或SMP
情况下才真正需要
在单CPU且不可抢占
内核下
自旋锁
所有操作都是空操作
个执行单元要想访问被自旋锁保护
共享资源
必须先得到锁
在访问完共享资源后
必须释放锁
如果在获取自旋锁时
没有任何执行单元保持该锁
那么将立即得到锁;如果在获取自旋锁时锁已经有保持者
那么获取锁操作将自旋在那里
直到该自旋锁
保持者释放了锁
无论是互斥锁
还是自旋锁
在任何时刻
最多只能有
个保持者
也就说
在任何时刻最多只能有
个执行单元获得锁
自旋锁
实现和体系结构密切相关
代码
般通过汇编实现
定义在文件,实际用到
接口定义在文件夹 中, 自旋锁
API有:
CODE:spin_lock_init(x)
该宏用于
化自旋锁x
自旋锁在真正使用前必须先
化
该宏用于动态
化指定
CODE:DEFINE_SPINLOCK(x)
该宏声明
个自旋锁x并
化它
该宏在2.6.11中第
次被定义
在先前
内核中并没有该宏
CODE:SPIN_LOCK_UNLOCKED
该宏用于静态
化
个自旋锁
CODE:DEFINE_SPINLOCK(x)等同于spinlock_t x = SPIN_LOCK_UNLOCKEDspin_is_locked(x)
)
该宏用于判断自旋锁x是否已经被某执行单元保持(即被锁)
如果是
返回真
否则返回假
CODE:spin_unlock_wait(x)
该宏用于等待自旋锁x变得没有被任何执行单元保持
如果没有任何执行单元保持该自旋锁
该宏立即返回
否则将循环在那里
直到该自旋锁被保持者释放
CODE:spin_trylock(lock)
该宏尽力获得自旋锁lock
如果能立即获得锁
它获得锁并返回真
否则不能立即获得锁
立即返回假
它不会自旋等待lock被释放
CODE:spin_lock(lock)
该宏用于获得自旋锁lock
如果能够立即获得锁
它就马上返回
否则
它将自旋在那里
直到该自旋锁
保持者释放
这时
它获得锁并返回
总的
只有它获得锁才返回
CODE:spin_lock_irqsave(lock, flags)
该宏获得自旋锁
同时把标志寄存器
值保存到变量flags中并失效本地中断
CODE:spin_lock_irq(lock)
该宏类似于spin_lock_irqsave
只是该宏不保存标志寄存器
值
禁止本地中断并获取指定
锁
CODE:spin_lock_bh(lock)
该宏在得到自旋锁
同时失效本地软中断
CODE:spin_unlock(lock)
该宏释放自旋锁lock
它和spin_trylock或spin_lock配对使用
如果spin_trylock返回假
表明没有获得自旋锁
因此不必使用spin_unlock释放
CODE:spin_unlock_irqrestore(lock, flags)
该宏释放自旋锁lock
同时
也恢复标志寄存器
值为变量flags保存
值
它和spin_lock_irqsave配对使用
CODE:spin_unlock_irq(lock)
该宏释放自旋锁lock
同时
并激活本地中断
它和spin_lock_irq配对应用
CODE:spin_unlock_bh(lock)
该宏释放自旋锁lock
同时
也使能本地
软中断
它和spin_lock_bh配对使用
CODE:spin_trylock_irqsave(lock, flags)
该宏如果获得自旋锁lock
它也将保存标志寄存器
值到变量flags中
并且失效本地中断
如果没有获得锁
它什么也不做
因此如果能够立即获得锁
它等同于spin_lock_irqsave
如果不能获得锁
它等同于spin_trylock
如果该宏获得自旋锁lock
那需要使用spin_unlock_irqrestore来释放
CODE:spin_trylock_irq(lock)
该宏类似于spin_trylock_irqsave
只是该宏不保存标志寄存器
如果该宏获得自旋锁lock
需要使用spin_unlock_irq来释放
CODE:spin_trylock_bh(lock)
该宏如果获得了自旋锁
它也将失效本地软中断
如果得不到锁
它什么也不做
因此
如果得到了锁
它等同于spin_lock_bh
如果得不到锁
它等同于spin_trylock
如果该宏得到了自旋锁
需要使用spin_unlock_bh来释放
CODE:spin_can_lock(lock)
该宏用于判断自旋锁lock是否能够被锁
它实际是spin_is_locked取反
如果lock没有被锁
它返回真
否则
返回假
该宏在2.6.11中第
次被定义
在先前
内核中并没有该宏
自旋锁
基本使用如下:
CODE:spinlock_t myr_lock = SPIN_LOCK_UNLOCKED;
spin_lock(&myr_lock);
/*临界区*/
spin_unlock(&myr_lock);
自旋锁在同
时刻至多被
个执行线程持有
所以
个时刻只能有
个线程位于临界区
这就为多处理器提供了防止并发访问所需
保护机制
但是在单处理器上
编译
时候不会加入自旋锁
它仅仅被当作
个设置内核抢占机制是否被启用
开关
注意
Linux内核实现
自旋锁是不可递归
这
点区别于自旋锁在其他操作系统中
实现
如果你想得到
个你正持有
锁
你必须自旋
等待你自己释放这个锁
但是你处于自旋忙等待中
所以永远没有机会释放锁
于是你就被自己锁死了
定要注意!
自旋锁可以用在中断处理
中
但是在使用时
定要在获取锁的前
首先禁止本地中断(当前处理器上
中断)
否则中断处理
就可能打断正持有锁
内核代码
有可能会试图支争用这个已经被持有
自旋锁
这样
来
中断处理
就会自旋
等待该锁重新可用
但是锁
持有者在这个中断处理
执行完毕的前不可能运行
这就会造成双重请求死锁
自旋锁和下半部:由于下半部(中断
下半部)可以抢占进程上下文中
代码
所以当下半部和进程上下文共享数据时
必须对进程上下文中
共享数据进行保护
所以需要加锁
同时还要禁止下半部执行
同样
由于中断处理
可以抢占下半部
所以如果中断处理
和下半部共享数据
那么就必须在获取恰当
锁
同时还要禁止中断
对于软中断
无论是否同种类型
如果数据被软中断共享
那么它必须得到锁
保护
同种类型
两个软中断也可以同进运行在
个系统
多个处理器上
但是
同
个处理器上
个软中断绝不会抢占另
个软中断
因此
根本不需要禁止下半部
延伸阅读
最新评论