POSIX threads(簡稱Pthreads)是在多核平臺上進(jìn)行并行編程的一套常用的API。線程同步(Thread Synchronization)是并行編程中非常重要的通訊手段,其中最典型的應(yīng)用就是用Pthreads提供的鎖機(jī)制(lock)來對多個線程之間共 享的臨界區(qū)(Critical Section)進(jìn)行保護(hù)(另一種常用的同步機(jī)制是barrier)胁孙。
Pthreads提供了多種鎖機(jī)制:
(1) Mutex(互斥量):pthread_mutex_***
(2) Spin lock(自旋鎖):pthread_spin_***
(3) Condition Variable(條件變量):pthread_con_***
(4) Read/Write lock(讀寫鎖):pthread_rwlock_***
Pthreads提供的Mutex鎖操作相關(guān)的API主要有:
pthread_mutex_lock (pthread_mutex_t *mutex);
pthread_mutex_trylock (pthread_mutex_t *mutex);
pthread_mutex_unlock (pthread_mutex_t *mutex);
Pthreads提供的與Spin Lock鎖操作相關(guān)的API主要有:
pthread_spin_lock (pthread_spinlock_t *lock);
pthread_spin_trylock (pthread_spinlock_t *lock);
pthread_spin_unlock (pthread_spinlock_t *lock);
從 實現(xiàn)原理上來講揽涮,Mutex屬于sleep-waiting類型的鎖。例如在一個雙核的機(jī)器上有兩個線程(線程A和線程B),它們分別運行在Core0和 Core1上扇丛。假設(shè)線程A想要通過pthread_mutex_lock操作去得到一個臨界區(qū)的鎖筒严,而此時這個鎖正被線程B所持有丹泉,那么線程A就會被阻塞 (blocking),Core0 會在此時進(jìn)行上下文切換(Context Switch)將線程A置于等待隊列中鸭蛙,此時Core0就可以運行其他的任務(wù)(例如另一個線程C)而不必進(jìn)行忙等待摹恨。而Spin lock則不然,它屬于busy-waiting類型的鎖规惰,如果線程A是使用pthread_spin_lock操作去請求鎖睬塌,那么線程A就會一直在 Core0上進(jìn)行忙等待并不停的進(jìn)行鎖請求,直到得到這個鎖為止。
所以揩晴,自旋鎖一般用用多核的服務(wù)器勋陪。
自旋鎖(Spin lock)
自旋鎖與互斥鎖有點類似,只是自旋鎖不會引起調(diào)用者睡眠硫兰,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持诅愚,調(diào)用者就一直循環(huán)在那里看是 否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名劫映。其作用是為了解決某項資源的互斥使用违孝。因為自旋鎖不會引起調(diào)用者睡眠,所以自旋鎖的效率遠(yuǎn) 高于互斥鎖泳赋。雖然它的效率比互斥鎖高雌桑,但是它也有些不足之處:
1、自旋鎖一直占用CPU祖今,他在未獲得鎖的情況下校坑,一直運行--自旋,所以占用著CPU千诬,如果不能在很短的時 間內(nèi)獲得鎖耍目,這無疑會使CPU效率降低。
2徐绑、在用自旋鎖時有可能造成死鎖邪驮,當(dāng)遞歸調(diào)用時有可能造成死鎖,調(diào)用有些其他函數(shù)也可能造成死鎖傲茄,如 copy_to_user()毅访、copy_from_user()、kmalloc()等烫幕。
因此我們要慎重使用自旋鎖俺抽,自旋鎖只有在內(nèi)核可搶占式或SMP的情況下才真正需要,在單CPU且不可搶占式的內(nèi)核下较曼,自旋鎖的操作為空操作磷斧。自旋鎖適用于鎖使用者保持鎖時間比較短的情況下。
自旋鎖的用法如下:
首先定義:spinlock_t x;
然后初始化:spin_lock_init(spinlock_t *x);? //自旋鎖在真正使用前必須先初始化
在2.6.11內(nèi)核中將定義和初始化合并為一個宏:DEFINE_SPINLOCK(x)
獲得自旋鎖:spin_lock(x);? //只有在獲得鎖的情況下才返回捷犹,否則一直“自旋”
spin_trylock(x);? //如立即獲得鎖則返回真弛饭,否則立即返回假
釋放鎖:spin_unlock(x);
結(jié)合以上有以下代碼段:
spinlock_t lock;? ? ? ? //定義一個自旋鎖
spin_lock_init(&lock);
spin_lock(&lock);
.......? ? ? ? //臨界區(qū)
spin_unlock(&lock);? //釋放鎖
還有一些其他用法:
spin_is_locked(x)
// 該宏用于判斷自旋鎖x是否已經(jīng)被某執(zhí)行單元保持(即被鎖),如果是萍歉,? 返回真侣颂,否則返回假。
spin_unlock_wait(x)
// 該宏用于等待自旋鎖x變得沒有被任何執(zhí)行單元保持枪孩,如果沒有任何執(zhí)行單元保持該自旋鎖憔晒,該宏立即返回藻肄,否
//將循環(huán)? ? 在那里,直到該自旋鎖被保持者釋放拒担。
spin_lock_irqsave(lock, flags)
// 該宏獲得自旋鎖的同時把標(biāo)志寄存器的值保存到變量flags中并失效本地中//斷嘹屯。相當(dāng)于:spin_lock()+local_irq_save()
spin_unlock_irqrestore(lock, flags)
// 該宏釋放自旋鎖lock的同時,也恢復(fù)標(biāo)志寄存器的值為變量flags保存的//值从撼。它與spin_lock_irqsave配對使用州弟。
//相當(dāng)于:spin_unlock()+local_irq_restore()
spin_lock_irq(lock)
//該宏類似于spin_lock_irqsave,只是該宏不保存標(biāo)志寄存器的值低零。相當(dāng)? ? ? ? //于:spin_lock()+local_irq_disable()
spin_unlock_irq(lock)
//該宏釋放自旋鎖lock的同時婆翔,也使能本地中斷。它與spin_lock_irq配對應(yīng)用掏婶。相當(dāng)于: spin_unlock()+local_irq+enable()
spin_lock_bh(lock)
// 該宏在得到自旋鎖的同時失效本地軟中斷啃奴。相當(dāng)于:? //spin_lock()+local_bh_disable()
spin_unlock_bh(lock)
//該宏釋放自旋鎖lock的同時,也使能本地的軟中斷气堕。它與spin_lock_bh配對//使用纺腊。相當(dāng)于:spin_unlock()+local_bh_enable()
spin_trylock_irqsave(lock, flags)
//該宏如果獲得自旋鎖lock,它也將保存標(biāo)志寄存器的值到變量flags中茎芭,并且失//效本地中斷,如果沒有獲得鎖誓沸,它什么也不做梅桩。因此如果能夠立即 獲得鎖,它等//同于spin_lock_irqsave拜隧,如果不能獲得鎖宿百,它等同于spin_trylock。如果該宏//獲得自旋鎖lock洪添,那需要 使用spin_unlock_irqrestore來釋放垦页。
spin_trylock_irq(lock)
//該宏類似于spin_trylock_irqsave,只是該宏不保存標(biāo)志寄存器干奢。如果該宏獲得自旋鎖lock痊焊,需要使用spin_unlock_irq來釋放。
spin_trylock_bh(lock)
// 該宏如果獲得了自旋鎖忿峻,它也將失效本地軟中斷薄啥。如果得不到鎖,它什么//也不做逛尚。因此垄惧,如果得到了鎖,它等同于spin_lock_bh绰寞,如果得 不到鎖到逊,它等同//于spin_trylock铣口。如果該宏得到了自旋鎖,需要使用spin_unlock_bh來釋放觉壶。
spin_can_lock(lock)
// 該宏用于判斷自旋鎖lock是否能夠被鎖枷踏,它實際是spin_is_locked取反。//如果lock沒有被鎖掰曾,它返回真旭蠕,否則,返回 假旷坦。該宏在2.6.11中第一次被定義掏熬,在//先前的內(nèi)核中并沒有該宏。