自旋锁
自己原地打转,等待资源可用,一旦可用就上锁霸占它。
- 多SMP系统中,CPU x原地打转,CPUy会解锁。
- 单CPU中,自旋锁就没有自选功能了,只有禁止抢占、禁止中断
自旋锁的内核结构体
spinlock对应的结构体如下定义,不同的架构可能有不同的实现:
//include/linux/spinlock_types.h
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;
//include/linux/spinlock_types.h
typedef struct raw_spinlock {
arch_spinlock_t raw_lock; //定义了依据arch结构的锁
#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;
//arch/arm/include/asm/spinlock_types.h
typedef struct {
union {
u32 slock;
struct __raw_tickets {
#ifdef __ARMEB__
u16 next; //next和owner是SMP中实现spinlock的关键
u16 owner;
#else
u16 owner;
u16 next;
#endif
} tickets;
};
} arch_spinlock_t;
spinlock在UP系统中的实现
在单CPU系统中, spin_lock的实现
// include/linux/spinlock.h
static __always_inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
}
#define raw_spin_lock(lock) _raw_spin_lock(lock)
// include/linux/spinlock_api_up.h
#define _raw_spin_lock(lock) __LOCK(lock) //单CPU中的内核实现
#define __LOCK(lock) \
do { preempt_disable(); ___LOCK(lock); } while (0) //禁止抢占
#define ___LOCK(lock) \
do { __acquire(lock); (void)(lock); } while (0)
// include/linux/compiler.h
#ifdef __CHECKER__
# define __acquire(x) __context__(x,1)
#else
# define __acquire(x) (void)0 //最后__acquire是一个空函数
在单CPU中spin_lock()退化为prrmpt_disable(),如果用的内核不支持preempt,那么spin_lock()什么都不做。
单CPU系统中,spin_lock_irq()的实现
static __always_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)
#define _raw_spin_lock_irq(lock) __LOCK_IRQ(lock)
#define __LOCK_IRQ(lock) \
do { local_irq_disable(); __LOCK(lock); } while (0) //这里先关中断,在调用__LOCK()
#define __LOCK(lock) \
do { preempt_disable(); ___LOCK(lock); } while (0) //接着调用preempt_disable()
单CPU系统中,spin_lock_bh()的实现
static __always_inline void spin_lock_bh(spinlock_t *lock)
{
raw_spin_lock_bh(&lock->rlock);
}
#define raw_spin_lock_bh(lock) _raw_spin_lock_bh(lock)
#define _raw_spin_lock_bh(lock) __LOCK_BH(lock)
#define __LOCK_BH(lock) \
do { __local_bh_disable_ip(_THIS_IP_, SOFTIRQ_LOCK_OFFSET); ___LOCK(lock); } while (0)
/*
* The preempt_count offset needed for things like:
*
* spin_lock_bh()
*
* Which need to disable both preemption (CONFIG_PREEMPT_COUNT) and //这里既禁止preempt,又禁止softirq
* softirqs, such that unlock sequences of:
*
* spin_unlock();
* local_bh_enable();
*
* Work as expected.
*/
#define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_LOCK_OFFSET)
单CPU系统中,spin_lock_irqsave()的实现
#define __LOCK_IRQSAVE(lock, flags) \
do { local_irq_save(flags); __LOCK(lock); } while (0) //这里和irq类似,就是先irqsave在preempt_disable()
#define __LOCK(lock) \
do { preempt_disable(); ___LOCK(lock); } while (0)
spinlock在SMP系统中的实现
在ARMv6及以上的ARM架构中,支持SMP系统,它的spinlock结构体定义如下:
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;
typedef struct {
union {
u32 slock;
struct __raw_tickets {
#ifdef __ARMEB__
u16 next;
u16 owner;
#else
u16 owner;
u16 next;
#endif
} tickets;
};
} arch_spinlock_t;
//旧版本的内核spin lock结构体只有一个u32 ,为0则可以获得锁,不为0就自旋等待。 新的自旋锁加了next和owner让线程可以按获取锁的顺序排队。
在多CPU系统中, spin_lock的实现
// include/linux/spinlock.h
#define raw_spin_lock(lock) _raw_spin_lock(lock)
// include/llocking/spinlock.c
#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif
// include/linux/spinlock_api_smp.h
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);
}
// include/linux/spinlock.h
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
__acquire(lock);
arch_spin_lock(&lock->raw_lock);
}
// arch/arm/include/asm/spinlock.h
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned long tmp;
u32 newval;
arch_spinlock_t lockval;
prefetchw(&lock->slock);
__asm__ __volatile__(
"1: ldrex %0, [%3]\n" //读取lock->slock
" add %1, %0, %4\n" //新取的加1
" strex %2, %1, [%3]\n" //新的内容写入到newval中
" teq %2, #0\n" //如果之前的先被取号了,回写失败,需要重新获取锁
" bne 1b"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
: "cc");
while (lockval.tickets.next != lockval.tickets.owner) { //不是要等的,相等时跳出
wfe(); //原地休息一会,CPU低功耗运行
lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); //一定间隔再看一下owner
}
//跳出循环了说明获得了锁 owner相等了
smp_mb();
}
访问临界资源后,调用spin_unlock
// arch/arm/include/asm/spinlock.h
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
smp_mb();
lock->tickets.owner++; //不可能有多个程序同时修改owner,所以不需要加什么互斥措施
dsb_sev(); //假如有其他程序正在spin_lock函数中循环,就立刻判断手上next是否等于lock->tickets.owner,如果相等就获得锁
}
参考资料
深入分析_linux_spinlock_实现机制
https://blog.csdn.net/electrombile/article/details/51289813
深入分析Linux自旋锁
http://blog.chinaunix.net/uid-20543672-id-3252604.html
Linux内核同步机制之(四):spin lock
http://www.wowotech.net/kernel_synchronization/spinlock.html