参考 Linux内核同步机制之(四):spin lock
内核锁定技术
What exactly are “spin-locks”?
X86与ARM中的原子操作-原理及实现
原子操作对同步与互斥的意义
LINUX KERNEL SPINLOCK使用不当的后果
Linux 内核的排队自旋锁
概念 wikepedia
自旋锁是计算机科学用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。
自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。因此操作系统的实现在很多地方往往用自旋锁。Windows操作系统提供的轻型读写锁(SRW Lock)内部就用了自旋锁。显然,单核CPU不适于使用自旋锁,这里的单核CPU指的是单核单线程的CPU,因为,在同一时间只有一个线程是处在运行状态,假设运行线程A发现无法获取锁,只能等待解锁,但因为A自身不挂起,所以那个持有锁的线程B没有办法进入运行状态,只能等到操作系统分给A的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。
获取、释放自旋锁,实际上是读写自旋锁的存储内存或寄存器。因此这种读写操作必须是原子的。通常用test-and-set等原子操作来实现
上面说通常使用TSL指令实现,之前总结过TSL指令是同步的硬件实现方式,会锁住CPU的地址总线,所以是对多核CPU生效的,并且内部实现是忙等待机制。
数据结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 typedef struct spinlock { union { struct raw_spinlock rlock ; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct { u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map ; }; #endif }; } spinlock_t ; typedef struct raw_spinlock { arch_spinlock_t raw_lock; #ifdef CONFIG_GENERIC_LOCKBREAK unsigned int break_lock; #endif #ifdef CONFIG_DEBUG_SPINLOCK unsigned int magic, owner_cpu; void *owner; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map ; #endif } raw_spinlock_t ;
函数调用 spin_lock/spin_unlock — 禁止内核抢占 spin_lock_irq/spin_unlock_irq — 禁止内核抢占并屏蔽中断 spin_lock_irqsave/spin_unlock_irqrestore — 禁止内核抢占并屏蔽中断,事先保存中断屏蔽位并事后恢复原状
内核版本: VERSION = 3 PATCHLEVEL = 10 SUBLEVEL = 1
spin_lock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 static inline void spin_lock (spinlock_t *lock) { raw_spin_lock(&lock->rlock); } #define raw_spin_lock(lock) _raw_spin_lock(lock) #ifndef CONFIG_INLINE_SPIN_LOCK void __lockfunc _raw_spin_lock(raw_spinlock_t *lock){ __raw_spin_lock(lock); } static inline void __raw_spin_lock(raw_spinlock_t *lock){ unsigned long tmp; __asm__ __volatile__( " movi %0, 0\n" " wsr %0, scompare1\n" "1: movi %0, 1\n" " s32c1i %0, %1, 0\n" " bnez %0, 1b\n" : "=&a" (tmp) : "a" (&lock->slock) : "memory" ); } static inline void __raw_spin_lock(raw_spinlock_t *lock){ preempt_disable(); spin_acquire(&lock->dep_map, 0 , 0 , _RET_IP_); LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); }
spin_unlock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static inline void spin_unlock (spinlock_t *lock) { raw_spin_unlock(&lock->rlock); } #define raw_spin_unlock(lock) _raw_spin_unlock(lock) #ifdef CONFIG_UNINLINE_SPIN_UNLOCK void __lockfunc _raw_spin_unlock(raw_spinlock_t *lock){ __raw_spin_unlock(lock); } static inline void __raw_spin_unlock(raw_spinlock_t *lock){ unsigned long tmp; __asm__ __volatile__( " movi %0, 0\n" " s32ri %0, %1, 0\n" : "=&a" (tmp) : "a" (&lock->slock) : "memory" ); } static inline void __raw_spin_unlock(raw_spinlock_t *lock){ spin_release(&lock->dep_map, 1 , _RET_IP_); do_raw_spin_unlock(lock); preempt_enable(); }
spin_lock_irq 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline void spin_lock_irq (spinlock_t *lock) { raw_spin_lock_irq(&lock->rlock); } #define raw_spin_lock_irq(lock) _raw_spin_lock_irq(lock) #ifndef CONFIG_INLINE_SPIN_LOCK_IRQ void __lockfunc _raw_spin_lock_irq(raw_spinlock_t *lock){ __raw_spin_lock_irq(lock); } static inline void __raw_spin_lock_irq(raw_spinlock_t *lock){ local_irq_disable(); preempt_disable(); spin_acquire(&lock->dep_map, 0 , 0 , _RET_IP_); LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); }
spin_unlock_irq 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline void spin_unlock_irq (spinlock_t *lock) { raw_spin_unlock_irq(&lock->rlock); } #define raw_spin_unlock_irq(lock) _raw_spin_unlock_irq(lock) #ifndef CONFIG_INLINE_SPIN_UNLOCK_IRQ void __lockfunc _raw_spin_unlock_irq(raw_spinlock_t *lock){ __raw_spin_unlock_irq(lock); } static inline void __raw_spin_unlock_irq(raw_spinlock_t *lock){ spin_release(&lock->dep_map, 1 , _RET_IP_); do_raw_spin_unlock(lock); local_irq_enable(); preempt_enable(); }
spin_lock_irqsave 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #define spin_lock_irqsave(lock, flags) \ do { \ raw_spin_lock_irqsave(spinlock_check(lock), flags); \ } while (0 ) #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) #define raw_spin_lock_irqsave(lock, flags) \ do { \ typecheck(unsigned long , flags); \ flags = _raw_spin_lock_irqsave(lock); \ } while (0 ) #else #define raw_spin_lock_irqsave(lock, flags) \ do { \ typecheck(unsigned long , flags); \ _raw_spin_lock_irqsave(lock, flags); \ } while (0 ) #define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags) #ifdef CONFIG_INLINE_SPIN_LOCK_IRQSAVE #define _raw_spin_lock_irqsave(lock) __raw_spin_lock_irqsave(lock) #endif #if !defined(CONFIG_GENERIC_LOCKBREAK) || defined(CONFIG_DEBUG_LOCK_ALLOC) static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock){ unsigned long flags; local_irq_save(flags); preempt_disable(); spin_acquire(&lock->dep_map, 0 , 0 , _RET_IP_); #ifdef CONFIG_LOCKDEP LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); #else do_raw_spin_lock_flags(lock, &flags); #endif return flags; } ... #endif
spin_unlock_irqrestore 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static inline void spin_unlock_irqrestore (spinlock_t *lock, unsigned long flags) { raw_spin_unlock_irqrestore(&lock->rlock, flags); } #define raw_spin_unlock_irqrestore(lock, flags) \ do { \ typecheck(unsigned long , flags); \ _raw_spin_unlock_irqrestore(lock, flags); \ } while (0 ) #define _raw_spin_unlock_irqrestore(lock, flags) \ __UNLOCK_IRQRESTORE(lock, flags) #ifndef CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE void __lockfunc _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags){ __raw_spin_unlock_irqrestore(lock, flags); } static inline void __raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags) { spin_release(&lock->dep_map, 1 , _RET_IP_); do_raw_spin_unlock(lock); local_irq_restore(flags); preempt_enable(); }
总结
spinlock比较底层,跟踪代码有多种实现不好界定,目前使用的比较少,就不继续深究了
spinlock可以禁用内核抢占,也可以禁用中断
spinlock底层通常用TSL实现
spinlock对多核cpu有效(TSL)
uni core不适用spinlock,需要等待时间片用尽然后等待其他进程释放锁,代价高
spinlock是busy waiting,没有进程切换的代价,但是占用cpu,所以加锁时间不宜过长
ending