Linux內(nèi)核同步機制的自旋鎖原理
一、自旋鎖
自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它在內(nèi)核中大量應用于中斷處理等部分(對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關閉中斷的方式,即在標志寄存器中關閉/打開中斷標志位,不需要自旋鎖)。
自旋就是自己連續(xù)的循環(huán)等待。如果你有抱著你的愛人旋轉的經(jīng)歷,那么你應該知道一件事情,為了安全,你不能旋轉太久,你的愛人如果頭昏,也想你早日釋放。是的,自旋的缺點,就是它頻繁的循環(huán)直到等待鎖的釋放,將它用于可以快速完成的代碼中才好。
自旋不能搶占,但能中斷。
相關話題:SMP和cpu。多個cpu和單個cpu。很多書說自旋鎖只能在多處理機中使用,這是不正確的。
首先定義
Spinlock_t lock;
對不起,我只能找到arm平臺的鎖了
/*
* ARMv6 Spin-locking.
*
* We (exclusively) read the old value, and decrement it. If it
* hits zero, we may have won the lock, so we try (exclusively)
* storing it.
*
* Unlocked value: 0
* Locked value: 1
*/
typedef struct {
volatile unsigned int lock;
#ifdef CONFIG_PREEMPT
unsigned int break_lock;
#endif
} spinlock_t;
#define SPINLOCK_MAGIC 0x1D244B3C
typedef struct {
unsigned long magic;
volatile unsigned long lock;
volatile unsigned int babble;
const char *module; // 所屬模塊
char *owner;
int oline;
} spinlock_t;
Lock為0時可以用,1是等待。0像鎖孔,當沒有鑰匙插進去時,它才可以插進去
怎么初始化呢?
#define spin_lock_init(x)
do {
(x)->magic = SPINLOCK_MAGIC;
(x)->lock = 0; ;0初始化,表示可用
(x)->babble = 5;
(x)->module = __FILE__;
(x)->owner = NULL;
(x)->oline = 0;
} while (0)
定義一個自旋鎖的方法很有意思,Spinlock_t lock=?????
可以通過spin_lock
Spin_lock_irqsave 來調(diào)用自旋鎖,后者不允許中斷。前者有可能在上鎖中發(fā)生中斷。
還有spin_trylock 這是一個絕不妥協(xié)的函數(shù),它不等待。
恢復為spin_unlock
Spin_unlock_irqrestore
考查下面代碼
#define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(lock, flags)
#define _spin_lock_irqsave(lock, flags)
do {
local_irq_save(flags); 保存中斷請求標志
preempt_disable(); 不允許搶占
_raw_spin_lock(lock);
__acquire(lock);
} while (0)
二、自旋鎖綜合使用
下面是一個使用的例子,你可以使用source insight查到它
/* never called when PTRS_PER_PMD > 1 */
void pgd_dtor(void *pgd, kmem_cache_t *cache, unsigned long unused)
{
unsigned long flags; /* can be called from interrupt context */
spin_lock_irqsave(&pgd_lock, flags); 枷鎖
pgd_list_del(pgd);
spin_unlock_irqrestore(&pgd_lock, flags); 釋放
}
中斷枷鎖
#define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(lock, flags)
分析
unsigned long __lockfunc _spin_lock_irqsave(spinlock_t *lock)
{
unsigned long flags;
local_irq_save(flags); 將寄存器存入flags,并關中斷
preempt_disable(); 搶占鎖
_raw_spin_lock_flags(lock, flags); 枷鎖
return flags;
}
EXPORT_SYMBOL(_spin_lock_irqsave);
/* For spinlocks etc */
#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
將標志寄存器的內(nèi)容放在內(nèi)存x中。請查看gcc匯編
static inline void _raw_spin_lock_flags (spinlock_t *lock, unsigned long flags)
{
#ifdef CONFIG_DEBUG_SPINLOCK
if (unlikely(lock->magic != SPINLOCK_MAGIC)) {
printk("eip: %p ", __builtin_return_address(0));
BUG();
}
#endif
__asm__ __volatile__(
spin_lock_string_flags
:"=m" (lock->slock) : "r" (flags) : "memory");
}
#define spin_lock_string_flags
" 1: "
"lock ; decb %0 " ;lock總線鎖住,原子操作
"jns 4f "
"2: "
"testl $0x200, %1 "
"jz 3f "
"sti "
"3: "
"rep;nop "
"cmpb $0, %0 "
"jle 3b "
"cli "
"jmp 1b "
"4: "
理解一下大概意思,就可以了。當lock-1后大于等于0就可以關中斷繼續(xù)執(zhí)行了,否則nop空操作。Nop期間,cpu可以執(zhí)行其他任務的代碼。
解鎖
#define spin_unlock_irqrestore(lock, flags) _spin_unlock_irqrestore(lock, flags)
void __lockfunc _spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
_raw_spin_unlock(lock);
local_irq_restore(flags);
preempt_enable();
}
static inline void _raw_spin_unlock(spinlock_t *lock)
{
#ifdef CONFIG_DEBUG_SPINLOCK
BUG_ON(lock->magic != SPINLOCK_MAGIC);
[!--empirenews.page--]BUG_ON(!spin_is_locked(lock));
#endif
__asm__ __volatile__(
spin_unlock_string
);
}
Raw赤裸的解鎖,表示最低沉的解鎖原理。
#define spin_unlock_string
"xchgb %b0, %1"
:"=q" (oldval), "=m" (lock->slock)
:"0" (oldval) : "memory"
加1.解鎖