1. 定义
一个自旋锁是一个互斥设备,它只能有两个值:“锁定”和“解锁”。如果锁可用,则“锁定”位被设置,而代码继续进入临界区;相反,如果锁被其他人获得,则代码进入忙循环并重复检查这个锁,直到该锁可用为止。这个循环为“自旋”
2. 实现
"\n1:\t" lock " ; decb %0\n\t" "jns 3f\n" "2:\t" "rep;nop\n\t" "cmpb $0,%0\n\t" "jle 2b\n\t" "jmp 1b\n" "3:\n\t"
第2行:lock>slock自减一,前面的lock保证原子操作
第3行:如果lock>slock为非负数,跳转到3,获得锁
第5行:lock->slock为负数,锁 被其他线程抢占,等待
第6行:将lock->slock和0比较
第7行:判断lock->slock是否小于等于0,如果判断为真,跳转到2,继续等待
第8行:此时lock->slock已经大于0,跳转到1抢占锁
3. 各平台区别
自旋锁是为了多CPU而设计的,但单CPU抢占内核时也类似于SMP。但各种情况还有一些差别,下面分类讨论。
I. 单CPU非抢占:自旋锁空操作,完全剔除内核
II.单CPU抢占:禁止CPU抢占,无自旋锁
III.多CPU非抢占:自旋锁。
IV.多CPU抢占:禁止CPU抢占,有自旋锁。
问 : 在单CPU非抢占中,为什么自旋锁会实现为空操作?
多线程之间共享数据,无自旋锁会引起并发访问吗?
答 : 如果非抢占单CPU进入某个锁的自旋状态,则会永远自旋下去。(这句话要好好理解)
如果多线程在访问共享数据的时候不休眠或者主动放弃CPU(schedule),不会引起并发访问。因为没有进行进行线程切换,另外一个线程不会访问此共享数据。这里要说一下中断的时候,如果中断和此线程共享数据,要先禁用中断,才能防止并发访问。
4. 自旋锁函数
I. spin_trylock(spinlock_t *lock)
在使用此函数的时候一定要注意检查返回值,如果返回失败,而又去修改共享数据,还是会引起竞争。相当于没有获取到锁,破门而入。
II.spin_lock_irqsave(spinlock_t *lock, unsigned long flags) 和spin_lock_bh(spinlock_t *lock)
进程上下文与下半部或中断共享数据。进程上下文会被softirq 或irq中断,如果没有禁用下半部或中断,在此进程上下文获得锁的情况下,此进程被中断,而中断又去取锁,会引起死锁。