自旋锁系列函数
| 函数 | 功能说明 |
|---|---|
| void spin_lock(spinlock_t *lock) | 进程和进程之间同步 |
| void spin_lock_bh(spinlock_t *lock) | 和本地软中断之间同步 |
| void spin_lock_irq(spinlock_t *lock) | 和本地硬件中断之间同步 |
| void spin_lock_irqsave(lock, flags) | 和本地硬件中断之间同步并保存本地中断状态 |
| int spin_trylock(spinlock_t *lock) | 尝试获取锁,如果成功返回非0值,否则返回0值 |
自旋锁特点
spinlock是一种死等的锁机制- 临界区执行时间短且不可睡眠,可以在中断上下文中使用。由于
spinlock死等的这种特性,如果临界区执行时间长,那么不断在临界区死等的执行单元会浪费CPU资源
自旋锁的公平性
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; // owner表示持有这个数字的thread可以获取自旋锁
u16 next; // next表示如果后续再有thread请求获取这个自旋锁,就给他分配这个数字
#endif
} tickets;
};
} arch_spinlock_t;
-
数据结构:spinlock_t --> raw_spinlock_t --> arch_spinlock_t
-
自旋锁公平调度实现方式
- 刚开始owner = next = 0
- 第一个thread获取spinlock,可以获取成功,此时owner = 0, next = 0
- 第二个thread获取spinlock,如果第一个thread还没有释放spinlock,则next++,next变为1
- 第三个thread获取spinlock,如果第一个thread还没有释放spinlock,则next++,next变为2
- 此时第一个thread释放spinlock,则执行owner++,owner = 1
- 虽然此时第二个thread和第三个thread都在等待spinlock,但是因为第二个thread的next=owner,所以第二个thread可以获取到spinlock,第三个thread则继续等待,这样保证了spinlock的唤醒机制是先到先唤醒,后到后唤醒,保证了公平性。
static inline void arch_spin_lock(arch_spinlock_t *lock) { unsigned long tmp; u32 newval; arch_spinlock_t lockval; prefetchw(&lock->slock); // 下面这段汇编翻译成C语言就是 /* lockval = lock->slock; newval = lockval + (1 << TICKET_SHIFT) <=等价于=> newval.ticket.next = lockval.tickets.next++ lockval = newval */ __asm__ __volatile__( "1: ldrex %0, [%3]\n" " add %1, %0, %4\n" " strex %2, %1, [%3]\n" " 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) { // 死等循环,直到 owner == next wfe(); // cpu进入低功耗状态 lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); } smp_mb(); }
参考链接
-
自旋锁Spinlock https://blog.csdn.net/longwang155069/article/details/52055876
-
linux内核开发第18讲:spinlock在SMP下的源码实现 https://www.bilibili.com/video/BV1Yh411R7YC