自旋锁

来源:互联网 发布:mac如何显示u盘图标 编辑:程序博客网 时间:2024/06/10 11:06

转自http://www.51testing.com/html/63/524463-818251.html

自旋锁:单处理器非抢占式内核和对称多处理器或抢占式内核。

    Linux 2.4.x及以前的版本都是非抢占式内核方式,如果编译成单处理器系统,在同一时间只有一个进程在执行,除非它自己放弃,不然只有通过"中断"才能中断其执行。因此,在单处理器非抢占式内核中,如果需要修改某个重要的数据结构,或者执行某些关键代码,只需要禁止中断。但是在对称多处理器,仅仅禁止某个CPU的中断是不够的,当然我们也可以将所有CPU的中断都禁止,但这样做开销很大,整个系统的性能会明显下降。

此外,即使在单处理器上,如果内核是抢占式的,也可能出现不同进程上下文同时进入临界区的情况。为此,Linux内核中提供了"自旋锁(spinlock)"的同步机制。 自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"因此而得名。因此,中断(或软中断)禁止用于防止同一CPU上中断(或软中断)对共享资源的非同步访问。而自旋锁则防止在不同CPU上的执行单元对共享资源的同时访问,以及不同进程上下文互相抢占导致的对共享资源的非同步访问。

我们Linux 2.4.21为基础,分析x86平台下自旋锁的类型及应用方式,相关代码在源代码树的include/linux/spinlock.h以及include/asm-i386/spinlock.h中。

Linux内核中的自旋锁在Linux内核中,自旋锁的基本使用方式如下:先声明一个spinlock_t类型的自旋锁变量,并初始化为"未加锁"状态。在进入临界区之前,调用加锁函数获得锁,在退出临界区之前,调用解锁函数释放锁。例如:

spinlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock(&lock);/* 临界区 */

spin_unlock(&lock);获得自旋锁和释放自旋锁的函数有多种变体。

spin_lock_irqsave/spin_unlock_irqrestore相对于自旋锁的其它函数组,这一组函数是最"安全"的,使用频率也最多。在调用spin_lock_irqsave之前,我们还需要声明一个unsign long类型的变量(例如flag),该函数可以顺序完成下列操作:

1. 将CPU的标志寄存器的内容保存在变量flag中;

2. 禁止CPU的本地中断;

3. 调用spin_lock获得自旋锁。而spin_unlock_irqrestore函数则在调用spin_unlock释放自旋锁之后,将变量flag保存的值恢复到CPU的标志寄存器中。

#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0)

#define local_irq_save(x) __save_and_cli(x)

#define __save_and_cli(x) do { __save_flags(x); __cli(); } while(0);

#define __save_flags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)

保存CPU的标志寄存器方法是:首先调用pushfl将标志寄存器压栈,再调用popl从栈中弹出保存在变量参数中。

#define __cli() __asm__ __volatile__("cli": : :"memory")禁止CPU的本地中断使用cli汇编指令。#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0)

#define local_irq_restore(x) __restore_flags(x)

#define __restore_flags(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc")

恢复CPU的标志寄存器方法是:首先调用pushl将变量参数压到栈中,再调用popfl从栈中弹出保存到标志寄存器。需要注意的是,这里没有显式执行开中断的动作。实际上,在标志寄存器中保持了原来的中断状态,在恢复寄存器的同时将中断也恢复到以前的状态。

spin_lock_irq/spin_unlock_irq和上面一组函数的不同在于,这一组函数并不涉及标志寄存器。spin_lock_irq函数首先禁止CPU的本地中断,再调用spin_lock获得自旋锁。而spin_unlock_irq函数则首先调用spin_unlock释放自旋锁,再打开CPU的本地中断。

#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0)

#define local_irq_disable() __cli()#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0)

#define local_irq_enable() __sti()

#define __sti() __asm__ __volatile__("sti": : :"memory")

打开CPU的本地中断使用sti汇编指令。spin_lock_bh/spin_unlock_bh/spin_trylock_bhspin_lock_bh函数在得到自旋锁的同时禁止本地软中断,spin_unlock_bh函数释放自旋锁的同时,也打开本地的软中断。
#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0)
#define local_bh_disable() cpu_bh_disable(smp_processor_id())
#define cpu_bh_disable(cpu) \do { local_bh_count(cpu)++; barrier(); } while (0)
#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0)
#define spin_trylock_bh(lock) ({ int __r; local_bh_disable();\__r = spin_trylock(lock); \if (!__r) local_bh_enable(); \__r; })
spin_lock/spin_unlock/spin_trylock上面各组函数最终都需要调用自旋锁操作函数。spin_lock函数用于获得自旋锁,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放。spin_unlock函数则释放自旋锁。此外,还有一个spin_trylock函数。尽力获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则不能立即获得锁,立即返回假。它不会自旋等待lock被释放。spin_lock/spin_unlock/spin_trylock在UP环境下由于Linux 2.4.x为不可抢占的内核,在单处理器环境下,自旋锁什么都不需要做。自旋锁类型spinlock_t设置为空,除了在GCC的早期版本中不支持内容为空的数据结构。typedef struct {} spinlock_t;相应地,自旋锁的操作函数不进行任何实质性处理。
#define spin_lock_init(lock) do { } while(0)
#define spin_lock(lock) (void)(lock) /* Not "unused variable". */
#define spin_is_locked(lock) (0)#define spin_trylock(lock) ({1; })
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
spin_lock/spin_unlock/spin_trylock在SMP环境下在SMP环境下,自旋锁类型spinlock_t含有一个unsigned int域lock。未加锁时值为1,加锁后值为0或负值。声明时也使用了volatile描述符,要求编译器不要对其作优化处理,对它的读写都需要从内存中访问。

typedef struct { volatile unsigned int lock;} spinlock_t;
static inline void spin_lock(spinlock_t *lock){ __asm__ __volatile__( spin_lock_string :"=m" (lock->lock) : : "memory");}

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 京东买了东西收货了不想要了怎么办 京东转卖的商品有问题怎么办 如果衣服下架了然后有退货怎么办 想买二手车可没有懂车的人怎么办 买车的时候异地车牌回家怎么办 天猫下单显示下单人数太多券怎么办 英雄联盟进入游戏后无限崩溃怎么办 打开电视显示百度影棒打不开怎么办 家里路由器网速一会快一会慢怎么办 用快看影视下载电影网速太慢怎么办 苹果手机下载东西网速特别慢怎么办 网上买重庆时时彩输了很多钱怎么办 找不到自己在哪个平台借过钱怎么办 九游账号绑定手机之前绑定的怎么办 九游充过钱的游戏忘了游戏名怎么办 百度网盘密码忘了申诉不了怎么办 手机号被别人注册了百度账号怎么办 快手被盗找回时出来重置密码怎么办 魅族账号密码和密保都忘记了怎么办 vivo账号的密保问题忘了怎么办 oppo账号密保问题忘了怎么办 小米手机刷了机忘了账号密码怎么办 忘了小米账号的密码是多少怎么办 千牛账号在手机上被限制登录怎么办 违规的千牛账号被限制登录了怎么办 苹果id和锁屏密码忘记了怎么办 感应门的编程密码忘记了怎么办 交易猫买的号被找回了怎么办 uc上我的小说看不了怎么办 微信零钱忘记密码没有银行卡怎么办 九游平台冻结提不了现怎么办 计算机考试报名登录名忘记了怎么办 云顶扑克提现怎么提不出来怎么办 微信正在下载一直0kb怎么办 守望先锋运行时出现意外错误怎么办 信用卡暂停使用怎么办还能恢复吗 新刷乳胶漆墙面一碰一个坑怎么办 夏天开空调冻着了头疼打喷嚏怎么办 桑蚕丝衣服被沐浴露退了色怎么办 空间被别人知道了密码登录了怎么办 三星手机显示解析包出现问题怎么办