自旋锁系列函数

函数 功能说明
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();
    }
    

参考链接