1. 程式人生 > >linux下核心態鎖與使用者態鎖詳細介紹

linux下核心態鎖與使用者態鎖詳細介紹

1 核心態下鎖

spinlock_t成為自旋鎖,它用在臨界區程式碼非常少的情況下。自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,如果釋放了該鎖,請求鎖的呼叫者可以立即得到它,繼續執行。自旋鎖可用於中斷服務程式之中。

初始化

spinlock_t使用前需進行初始化,自旋鎖的初始化有兩種方式:

  1. 靜態初始化 SPIN_LOCK_UNLOCKED

如:spinlock_tx lock = SPIN_LOCK_UNLOCKED

  1. 動態初始化spin_lock_init()

如:spin_lock_init( spinlock_tx* lock)

加鎖

spin_is_locked(lock)

該函式用於判斷自旋鎖lock是否已經被某執行單元保持(即被鎖),如果是,返回真,否則返回假。

spin_lock(lock)

該函式用於獲得自旋鎖lock,如果能夠立即獲得鎖,它就馬上返回,否則,它將自旋在那裡,直到該自旋鎖的保持者釋放,這時,它獲得鎖並返回。總之,只有它獲得鎖才返回。

spin_lock_irqsave(lock, flags)

該函式獲得自旋鎖的同時把標誌暫存器的值儲存到變數flags中並失效本地中斷。

spin_lock_irq(lock)

該函式類似於spin_lock_irqsave,只是該巨集不儲存標誌暫存器的值。

spin_lock_bh(lock)

該函式在得到自旋鎖的同時失效本地軟中斷。

spin_trylock(lock)

該函式盡力獲得自旋鎖lock,如果能立即獲得鎖,它獲得鎖並返回真,否則不能立即獲得鎖,立即返回假。它不會自旋等待lock被釋放。這是一個非阻塞自旋鎖操作。

spin_trylock_bh(lock)

該巨集如果獲得了自旋鎖,它也將失效本地軟中斷。如果得不到鎖,它什麼也不做。因此,如果得到了鎖,它等同於spin_lock_bh,如果得不到鎖,它等同於spin_trylock。如果該巨集得到了自旋鎖,需要使用spin_unlock_bh來釋放。

解鎖

spin_unlock(lock)

該巨集釋放自旋鎖lock,它與spin_trylock或spin_lock配對使用。如果spin_trylock返回假,表明沒有獲得自旋鎖,因此不必使用spin_unlock釋放。

spin_unlock_irqrestore(lock, flags)

該巨集釋放自旋鎖lock的同時,也恢復標誌暫存器的值為變數flags儲存的值。它與spin_lock_irqsave配對使用。

spin_unlock_irq(lock)

該巨集釋放自旋鎖lock的同時,也使能本地中斷。它與spin_lock_irq配對應用。

spin_unlock_bh(lock)

該巨集釋放自旋鎖lock的同時,也使能本地的軟中斷。它與spin_lock_bh配對使用。

1.2 mutex_t

互斥鎖(Mutex)是在原子操作API的基礎上實現的訊號量行為。互斥鎖不能進行遞迴鎖定或解鎖,能用於互動上下文但是不能用於中斷上下文,同一時間只能有一個任務持有互斥鎖,而且只有這個任務可以對互斥鎖進行解鎖。當無法獲取鎖時,執行緒進入睡眠等待狀態。

初始化

mutex_t使用前需進行初始化,自旋鎖的初始化有兩種方式:

  1. 靜態初始化 SPIN_LOCK_UNLOCKED

如:DEFINE_MUTEX(lock)

  1. 動態初始化 mutex_init()

如:mutex_init(struct mutex *lock)

加鎖

void mutex_lock(struct mutex *lock);

無法獲得鎖時,睡眠等待,不會被訊號中斷。

int mutex_trylock(struct mutex *lock);

此函式是mutex_lock()的非阻塞版本,成功返回1,失敗返回0。

int mutex_lock_interruptible(struct mutex *lock);

和mutex_lock()一樣,也是獲取互斥鎖。在獲得了互斥鎖或進入睡眠直到獲得互斥鎖之後會返回0。如果在等待獲取鎖的時候進入睡眠狀態收到一個訊號(被訊號打斷睡眠),則返回_EINIR。

解鎖

void mutex_unlock(struct mutex *lock);

訊號量在建立時需要設定一個初始值,表示同時可以有幾個任務可以訪問該訊號量保護的共享資源,初始值為1就變成互斥鎖(Mutex),即同時只能有一個任務可以訪問訊號量保護的共享資源。一個任務要想訪問共享資源,首先必須得到訊號量,獲取訊號量的操作將把訊號量的值減1,若當前訊號量的值為負數,表明無法獲得訊號量,該任務必須掛起在該訊號量的等待佇列等待該訊號量可用;若當前訊號量的值為非負數,表示可以獲得訊號量,因而可以立刻訪問被該訊號量保護的共享資源。當任務訪問完被訊號量保護的共享資源後,必須釋放訊號量,釋放訊號量通過把訊號量的值加1實現,如果訊號量的值為非正數,表明有任務等待當前訊號量,因此它也喚醒所有等待該訊號量的任務。

資料結構

struct semaphore

{

     atomic_t count;                                                                        //共享計數值

     int sleepers;                                                     //等待當前訊號量進入睡眠程序的個數

     wait_queue_head_t wait;                                                     //當前訊號量的等待佇列

};

初始化

void sema_init(struct semaphore *sem, int val);

該函式初始化訊號量,並設定訊號量的值為val。

獲得訊號量

void down(struct semaphore *sem);

該函式使用者獲得訊號量sem,它會導致睡眠,因此不能用在中斷上下文。

int down_interruptible(struct semaphore *sem);

該函式與down類似,不同之處,down不能被訊號打斷,down_interruptible可以被訊號打斷。

void down_trylock(struct semaphore *sem);

該函式嘗試獲得訊號量,如果能立即獲得,它就獲得該訊號量並返回0,否則,返回非0值。它不會導致呼叫者睡眠,可以在中斷上下文使用。

釋放訊號量

void up(struct semaphore *sem);

該函式釋放訊號量,喚醒等待者。

一個或多個任務可以併發地持有讀者鎖;相反,用於寫的鎖只能被一個寫任務持有,而且此時不能有併發地讀操作。讀寫鎖也叫做共享/排斥鎖,或者併發/排斥鎖,因為對於讀者而言是共享的,對於寫著以排斥的形式獲取。

資料結構

typedef struct

{

     Volatile unsigned int lock;

     #ifdef  CONFIG_DEBUG_SPINLOCK

     unsigned magic;

     #endif

     #ifdef CONFIG_PREEMPT  

/*表示程序正在忙等待自旋鎖,只有核心支援SMP和核心搶佔時才使用本標誌*/

     Unsigned int break_lock;

     #endif

}rwlock_t;

讀寫自旋鎖的lock分為兩個部分:

0-23位:表示併發讀的數量。資料以補碼的形式存放。

24位:未鎖標識。如果沒有讀或者寫設定時,清0.

如果寫者獲得了鎖,則鎖為0x0000 0000(未鎖標誌清0,表示已鎖但無讀者。如果一個或多個程序獲得了讀鎖,則鎖的值為0x00ff ffff, 0x00ff fffe鎖標誌清0)。

初始化

rwlock_init(lock);

動態初始化指定鎖。

rwlock_tx lock = RW_LOCK_UNLOCKED

靜態初始化指定鎖。

加鎖

加讀鎖:

read_trylock(lock)

讀者用它來盡力獲得讀寫鎖lock,如果能夠立即獲得讀寫鎖,它就獲得鎖並返回真,否則不能獲得鎖,返回假。無論是否能夠獲得鎖,它都將立即返回,絕不自旋在那裡。

read_lock(lock)

讀者要訪問被讀鎖lock保護的共享資源,需要使用該函式來得到讀鎖lock。如果能夠立即獲得,它將立即獲得讀鎖並返回,否則,將自旋在那裡,直到獲得該讀鎖。

read_lock_irqsave(lock, flags)

讀者也可以使用該函式來獲得讀鎖,與read_lock不同的是,該函式還同時把標誌暫存器的值儲存到了變數flags中,並失效了本地中斷。

read_lock_irq(lock)

讀者也可以用它來獲得讀鎖,與read_lock不同的是,該函式還同時失效了本地中斷。該函式與read_lock_irqsave的不同之處是,它沒有儲存標誌暫存器。

read_lock_bh(lock)

讀者也可以用它來獲得讀鎖,與read_lock不同的是,該函式還同時失效了本地的軟中斷。

加寫鎖:

write_trylock(lock)

寫者用它來盡力獲得寫鎖lock,如果能夠立即獲得寫鎖,它就獲得鎖並返回真,否則不能獲得鎖,返回假。無論是否能夠獲得鎖,它都將立即返回,絕不自旋在那裡。

write_lock(lock)

寫者要想訪問被寫鎖lock保護的共享資源,需要使用該巨集來得到寫鎖lock。如果能夠立即獲得,它將立即獲得寫鎖並返回,否則,將自旋在那裡,直到獲得該寫鎖。

write_lock_irqsave(lock, flags)

寫者可以用它來獲得寫鎖,與write_lock不同的是,該巨集還同時把標誌暫存器的值儲存到了變數flags中,並失效了本地中斷。

write_lock_irq(lock)

寫者也可以用它來獲得鎖,與write_lock不同的是,該巨集還同時失效了本地中斷。該巨集與write_lock_irqsave的不同之處是,它沒有儲存標誌暫存器。

write_lock_bh(lock)

寫者也可以用它來獲得寫鎖,與write_lock不同的是,該巨集還同時失效了本地的軟中斷。

釋放鎖

釋放讀鎖

read_unlock(lock)

讀者使用該函式來釋放讀鎖lock。它必須與read_lock配對使用。

read_unlock_irqrestore(lock, flags)

讀者也可以使用該函式來釋放讀鎖,與read_unlock不同的是,該函式還同時把標誌暫存器的值恢復為變數flags的值。它必須與read_lock_irqsave配對使用。

read_unlock_irq(lock)

讀者也可以使用該函式來釋放讀鎖,與read_unlock不同的是,該函式還同時使能本地中斷。它必須與read_lock_irq配對使用。

read_unlock_bh(lock)

讀者也可以使用該函式來釋放讀鎖,與read_unlock不同的是,該函式還同時使能本地軟中斷。它必須與read_lock_bh配對使用。

  釋放寫鎖

write_unlock(lock)

寫者使用該函式來釋放寫鎖lock。它必須與write_lock配對使用。

write_unlock_irqrestore(lock, flags)

寫者也可以使用該函式來釋放寫鎖,與write_unlock不同的是,該函式還同時把標誌暫存器的值恢復為變數flags的值,並使能本地中斷。它必須與write_lock_irqsave配對使用。

write_unlock_irq(lock)

寫者也可以使用該函式來釋放寫鎖,與write_unlock不同的是,該函式還同時使能本地中斷。它必須與write_lock_irq配對使用。

write_unlock_bh(lock)

寫者也可以使用該函式來釋放寫鎖,與write_unlock不同的是,該函式還同時使能本地軟中斷。它必須與write_lock_bh配對使用。

使用者態下鎖

自旋鎖最多可能被一個可執行執行緒所持有。一個被徵用的自旋鎖使得請求它的執行緒在等待鎖重新可用時自旋(特別浪費處理器時間)。所以自旋鎖不應該被長時間持有。自旋鎖是不可遞迴的!

初始化

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

分配使用自旋鎖lock所需要的資源,並且初始化鎖lock為未鎖狀態。

加鎖

int pthread_spin_lock(pthread_spinlock_t *lock);

鎖住自旋鎖lock。當鎖沒有被某個執行緒持有時,呼叫的執行緒將獲得鎖,否則執行緒將不斷自旋,知道鎖可用。

int pthread_spin_trylock(pthread_spinlock_t *lock);

 如果鎖沒有被某個執行緒持有,自旋鎖lock將被鎖住,否則將會失敗。

解鎖

int pthread_spin_unlock(pthread_spinlock_t *lock);

釋放被鎖住的自旋鎖lock。

銷燬

int pthread_spin_destroy(pthread_spinlock_t *lock);

銷燬自旋鎖lock,並且回收被鎖lock使用的任何資源。

2.2.2 pthread_mutex_t

pthread_mutex_t對應核心的spinlock_t。核心持有自旋鎖時不應休眠,而使用者態的pthread_mutex_t沒有此限制。在持有鎖時可以進行休眠的操作。

初始化

pthread_mutex_t使用前需進行初始化,自旋鎖的初始化有兩種方式:

1、靜態初始化PTHREAD_MUTEX_INITIALIZER

如:pthread_mutex_t g_stACLPubLock = PTHREAD_MUTEX_INITIALIZER;

2、動態初始化pthread_mutex_init()

如:pthread_mutex_init(pthread_mutex_t *stLock,  NULL)

加鎖

pthread_mutex_lock(pthread_mutex_t *stLock)

加鎖,如果鎖被佔用則一直阻塞,直到持有該鎖/異常原因導致失敗。

pthread_mutex_trylock(pthread_mutex_t *stLock)

加鎖,嘗試加鎖,如果鎖被佔用,則返回一個錯誤碼。

pthread_mutex_timedlock(pthread_mutex_t *stLock,const struct timespec tsptr)

加鎖,設定一個時間上限,如果超過時間仍然得不到該鎖,則返回一個錯誤碼。

解鎖

pthread_mutex_unlock(pthread_mutex_t *stLock)

解鎖。

銷燬

pthread_mutex_destroy(pthread_mutex_t *stLock)

銷燬鎖。

高階特性

pthread_mutex_init()可以傳入pthread_mutexattr_t,用來指明鎖的額外特性,例如:

PTHREAD_MUTEX_ERRORCHECK:開啟錯誤檢測,當重複釋放/重複獲取時,lock/unlock函式會返回錯誤碼。

使用例項如下:

pthread_mutex_init(&lock,0);

….

pthread_mutex_lock(&lock)

/*臨界區資源…*/

pthread_mutex_unlock(&lock)

pthread_mutex_destroy(&lock)

/*需要真正保護的是資料而不是程式碼,採用特定的鎖保護自己的共享資料*/

訊號量也就是作業系統中所用到的PV原語,它廣泛用於程序或執行緒間的同步與互斥。訊號量本質上是一個非負的整數計數器,它被用來控制對公共資源的訪問。訊號量分為有名訊號量和無名訊號量,無名訊號量主要用於執行緒間的互斥,有名訊號量用於程序間同步。下面主要介紹無名訊號量的相關函式,有名訊號量則放置下節的程序間同步。

初始化

sem_init()

sem_init()用來初始化一個semaphore。它的原型為:

extern int sem_init_P(sem_t* _sem, int _pshared, unsigned int _value);

sem為指向訊號量結構的一個指標,_pshared不為0時此時訊號量在程序間共享,否則只能為當前程序的所有執行緒共享,value給出了訊號量的初始值。

獲取

sem_wait(sem_t *sem)

獲取semaphore,如果獲取不到則阻塞,或者其他異常錯誤發生。被用來阻塞當前執行緒直到訊號量sem值大於0,解除阻塞後將sem值減一,表明公共資源經使用後減少。它會等到訊號量為一個大於0的值才開始減一。

sem_trywait(sem_t *sem)

獲取semaphore,如果獲取不到,則返回錯誤碼。為sem_wait()的非阻塞版本。它直接將訊號量的值減一。

sem_timedwait(sem_t *sem, const struct timespec tsptr)

獲取semaphore,如果獲取不到,則最多等待指定的時間tsptr,如果超時後仍然獲取不到,則返回錯誤碼。

釋放

sem_post(sem_t *sem, const struct timespec tsptr)

釋放指定的semaphore。等待在sem_wait()上的程序可以被喚醒。用來增加訊號量的值,當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中一個執行緒不在阻塞,選擇機制同樣由執行緒的排程策略決定的。

銷燬

sem_destroy(sem_t *sem)

銷燬semaphore。

pthread_rwlock_t對應核心的rwlock_t。核心持有rwlock_t時不能休眠,但是pthread_rwlock_t則無此限制。

初始化

pthread_rwlock_init(pthread_rwlock_t *lock)

pthread_rwlock_t使用前需要使用pthread_rwlock_init()進行初始化操作。

加鎖

       加讀鎖:

pthread_rwlock_rdlock(pthread_rwlock_t *lock)

       加讀鎖,如果鎖被佔用,則阻塞,直到持有該鎖或者異常原因導致失敗。

pthread_rwlock_tryrdlock(pthread_rwlock_t *lock)

       加讀鎖,如果鎖被佔用,則返回錯誤碼。

pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,const struct timespec tsptr)

       加讀鎖,如果鎖被佔用,則最長等待指定的時間,如果指定時間後仍然獲取不到鎖,則返回錯誤碼。

       加寫鎖:

pthread_rwlock_wrlock(pthread_rwlock_t *lock)

       加寫鎖,如果鎖被佔用,則阻塞,直到持有該鎖或者異常原因導致失敗。

pthread_rwlock_trywrlock(pthread_rwlock_t *lock)

       加寫鎖,如果鎖被佔用,則返回錯誤碼。

pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,const struct timespec tsptr)

       加寫鎖,如果鎖被佔用,則最長等待指定的時間,如果指定時間後仍然獲取不到鎖,則返回錯誤碼。

解鎖

pthread_rwlock_unlock(pthread_rwlock_t *lock)

       無論讀鎖還是寫鎖,解鎖時都使用pthread_rwlock_unlock()。

銷燬

pthread_rwlock_destroy(pthread_rwlock_t *lock);

高階特性

       pthread_rwlock_init時可以傳入pthread_mutexattr_t,用來指明鎖的額外特性。

使用例項如下:

pthread_rwlock_t lock;

pthread_rwlock_init(&lock)

….

pthread_rwlock_rdlock(&lock)

/*臨界區資源…*/

pthread_rwlock_unlock(&lock)

pthread_rwlock_destroy(&lock)

/*需要真正保護的是資料而不是程式碼,採用特定的鎖保護自己的共享資料*/

pthread_cond_t提供和semaphore類似的功能,不過無需指定類似semaphore的init value。條件變數是利用執行緒間共享的全域性變數進行同步的一種機制,主要是包括兩個動作:一個執行緒等待“條件變數的條件成立”而掛起;另一個執行緒使“條件成立”(給出條件成立訊號)。為了防止競爭,條件變數的使用總是和一個互斥鎖結合在一起,條件變數的型別為pthread_cond_t。

初始化

主要有動態和靜態初始化,動態初始化:

pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

pthread_cond_init()用來初始化一個條件變數。成功返回0,失敗返回錯誤碼。一般cond_attr定義為NULL且被忽略。

靜態初始化:

Pthread_cond_t cond=PTHREAD_COND_INTIALIZER;

等待

pthread_cond_wait()

用來等待一個條件變數滿足,呼叫時,引數的pthread_mutex_t必須被鎖住。函式返回時則cond滿足,且持有該pthread_mutex_t。若條件變數沒有滿足則會阻塞當前執行緒並釋放互斥鎖,直到條件變數滿足,此執行緒會先獲得互斥鎖並繼續往下執行。

pthread_cond_timedwait()

通pthread_cond_wait()類似,用來等待一個條件變數滿足,呼叫時,引數的pthread_mutex_t必須被鎖住。函式返回時則cond滿足或者超時(通過錯誤碼錶示),返回時持有該pthread_mutex_t。

這兩種等待方式都必須和一個互斥鎖配合,以防止多個執行緒同時請求wait()的競爭條件,互斥鎖必須是普通鎖或者適應鎖,且在呼叫pthread_cond_wait()前必須由本執行緒加鎖,而在更新條件等待佇列前,mutex保持鎖定狀態,並在執行緒掛起進入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex被重新加鎖,以與進入pthread_cond_wait()前加的鎖動作對應。

使用例項:

pthread_mutex_lock();

while(condition_is_false)

    pthread_cond_wait();

pthread_mutex_unlock();

它首先將當前執行緒加入到喚醒佇列,然後立即解鎖mutex,最後等待被喚醒,又對metux加鎖。

喚醒

pthread_cond_signal()

喚醒至少一個等待在cond上的執行緒,具體啟用哪些由排程策略決定。如果沒有等待在該cond上的執行緒,則什麼也不發生。

thread_cond_broadcast()

喚醒全部等待在cond上的執行緒。

銷燬

pthread_cond_destroy(pthread_cond_t *cond)

銷燬一個cond。成功返回0,失敗返回錯誤碼。只有在沒有執行緒在該條件變數上等待的時候才能有登出這個條件變數,否則返回busy。因為linux實現的條件變數沒有分配什麼資源,所以登出動作只包括檢查是否有等待執行緒。

3 各鎖用於程序間同步

3.1 互斥量屬性

我們已經知道了互斥量可以用於線上程間同步,但實際上,互斥量也可以用於程序間的同步。為了達到這一目的,可以在pthread_mutex_init初始化之前,修改其屬性為程序間共享。mutex的屬性修改函式主要有以下幾個:

pthread_mutexattr_t mattr 型別:                                                //用於定義互斥量的屬性

pthread_mutexattr_init函式:                                                 //初始化一個mutex屬性物件

pthread_mutexattr_destroy函式:                                      //銷燬mutex屬性物件 (而非銷燬鎖)

pthread_mutexattr_setpshared函式:                                                    //修改mutex屬性。

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);

我們重點看第二個引數:pshared,它有以下兩個取值:

執行緒鎖:PTHREAD_PROCESS_PRIVATE (mutex的預設屬性即為執行緒鎖,程序間私有)

程序鎖:PTHREAD_PROCESS_SHARED

要想實現程序間同步,需要將mutex的屬性改為PTHREAD_PROCESS_SHARED。

使用如下:

pthread_mutexattr_t ma;

pthread_mutex_t stLock;

pthread_mutexattr_init(&ma);

pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);

pthread_mutex_init(&stLock, &ma);

其中:mastLock需要初始化在共享記憶體或各程序都能訪問到的檔案中。具體見下面的小例子

注:若各個程序間沒有父子關係,例如有三個程序同時執行在共享記憶體中初始化鎖的步驟,很可能某兩個程序發現共享記憶體不存在,然後同時新建並初始化鎖。某一個 lock mutex,然後另外一個又 init mutex,就亂了。

可以使用如下方式建立共享記憶體:

shm_open(const char *shm_name, into flag,mode_t mode);

// 開啟或建立一個共享記憶體

if (link(const char * shm_name, const char * shm_name_new) == 0) {

   // 我成功建立了這片共享記憶體,則在此共享記憶體內初始化鎖

} else {

   // 別人已經建立了共享記憶體,不需要初始化鎖了

}

shm_unlink(shm_name);//使用link時,最後一定要unlink掉shm_name的副本

其實 /dev/shm 是一個 mount 了的檔案系統。這裡面放的就是一堆通過 shm_open 新建的共享記憶體。都是以檔案的形式展現出來。可以 rm,rename,link 各種檔案操作。

其次也可以使用其他方式,如使用shm_open(dir,O_RDWR,0777)檢測是否能夠開啟共享記憶體物件,若能開啟則,說明共享記憶體已初始化即鎖也得到了初始化,直接對映使用。若開啟失敗,則說明共享記憶體未建立,此時建立共享記憶體並初始化鎖。如下:

If(-1 == shm_open(dir,O_RDWR,0777))

{

      共享記憶體未建立,建立共享記憶體並初始化鎖。

}

else

{

      將共享記憶體直接對映到本程序,並直接使用鎖即可

}

這裡給出程序同步互斥鎖初始化流程:

1、建立共享記憶體結構體

struct shm_mutex

{

         pthread_mutex_t mutex;

         pthread_mutexattr_t mutexattr;

          ……

          ……

    };

    struct shm_mutex   *pic_mutex;

 2、申請共享記憶體空間

      //程序間存在父子關係,若程序間不存在父子關係則使用上面提到的方式申請共享記憶體

    int fd = shm_open(const char filename, O_CREAT|O_RDWR,0777);

    ftruncate(fd,sizeof(*pic_mutex));                                               //改變檔案大小

pic_mutex = mmap(NULL,sizeof(struct shm_mutex),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  

//使用者地址對映到核心空間

    close(fd);

    memset(pic_mutex,0,sizeof(*pic_mutex));

3、設定共享記憶體程序間屬性

    pthread_mutexattr_init(&pic_mutex->mutexattr);

   pthread_mutexattr_setpshared(&pic_mutex->mutexattr,PTHREAD_PROCESS_SHARED);//此句將執行緒鎖改變為程序間共用屬性

  pthread_mutex_init(&pic_mutex->mutex,&pic_mutex->mutexattr);

在此執行緒鎖的程序間共用屬性設定完畢,即可對其進行上鎖解鎖操作

 4、使用鎖

   pthread_mutex_lock(&pic_mutex->mutex);                                                   //上鎖

   …………

  pthread_mutex_unlock(&pic_mutex->mutex);                                                //解鎖

   5、銷燬鎖

    pthread_mutexattr_destroy(&pic_mutex->mutexattr);

pthread_mutex_destroy(&pic_mutex->mutex);

注:這裡是將鎖初始化在共享記憶體中,也可以不初始化在共享記憶體中,只需將鎖初始化在各個程序都能訪問到了地址空間即可。

這裡給出互斥屬性用於使用程序間同步的小例子。這裡的例子程序間不存在父子關係。

3.2 讀寫鎖屬性

讀寫鎖也有屬性,使用pthread_rwlocksttr_init初始化pthread_rwlocksttr_t結構,再用pthread_rwlocksttr_destroy回收結構。讀寫鎖唯一支援的屬性是程序共享屬性,該屬性與互斥量的程序共享屬性相同,就像互斥量的程序共享屬性一樣,用一對函式來讀取和設定讀寫鎖的程序屬性。

主要應用函式:

Pthread_rwlocksttr_t rwlock 型別:                                            //用於定義讀寫鎖的屬性

pthread_rwlocksttr_init函式:                                             //初始化一個rwlock屬性物件

pthread_rwlocksttr_destroy函式:                                   // 銷燬rwlock屬性物件 (而非銷燬鎖)

pthread_rwlocksttr_setpshared函式:                                                 //修改rwlock屬性。

int pthread_rwlocksttr_setpshared(pthread_rwlocksttr_t *attr, int pshared);

我們重點看第二個引數:pshared,它只有一個取值:

程序鎖:PTHREAD_PROCESS_SHARED

要想實現程序間同步,需要將rwlock的屬性設為PTHREAD_PROCESS_SHARED。

它的使用方式與上面說的互斥鎖是一致的,也是建立共享記憶體結構體、申請共享記憶體空間、設定共享記憶體程序間屬性、使用鎖和銷燬鎖。

注:若各個程序間沒有父子關係,則也是用在互斥鎖中提到的建立共享記憶體方式建立共享記憶體來初始化鎖。

3.3 條件變數屬性

Single UNIX Specification目前定義了條件變數的兩個屬性:程序共享屬性和時鐘屬性。與其它屬性物件一樣,有一對函式用於初始化和反初始化的條件變數屬性。

主要應用函式:

Pthread_condattr_t attr 型別:                                            //用於定義條件變數的屬性

pthread_ condattr_t _init函式:                                           //初始化一個attr屬性物件

pthread_ condattr_destroy函式:                                    // 銷燬attr屬性物件 (而非銷燬鎖)

pthread_condattr_setpshared函式:                                                   //修改attr屬性。

與其它的同步屬性一樣,條件變數支援程序共享屬性。它控制著條件變數可以是被單個程序的多個執行緒使用,也可以被多個程序的執行緒使用。要設定程序共享屬性的當前值,可以使用pthread_condattr_setpshared函式將屬性值設為PTHREAD_PROCESS_SHARED。

注:當屬性值用於程序共享時,與其使用的互斥鎖也要設定成程序間共享屬性。

3.4 有名訊號量

有名訊號量的特點是把訊號量的值儲存在檔案中。這決定了它的用途非常廣:既可以用於執行緒,也可以用於相關程序間,甚至是不相關程序。由於有名訊號量的值是儲存在檔案中的,所以對於相關程序來說,子程序是繼承了父程序的檔案描述符,那麼子程序所繼承的檔案描述符所指向的檔案是和父程序一樣的,當然檔案裡面儲存的有名訊號量值就共享了。

       有名訊號量是位於共享記憶體區的(/dev/shm),那麼它要保護的資源也必須是位於共享記憶體區,只有這樣才能被無相關的程序所共享。

初始化

sem_t *sem_open(const char *name, int flag)                   

 //開啟一個有名訊號量,此有名訊號量已經存在

 sem_t *sem_open(const char *name, int flag,mode_t mode, unsigned int value)  

//建立一個有名訊號量

返回值:若成功,則返回訊號量的地址;若出錯,返回SEM_FAILED

引數:

name:訊號量的檔名。

flags:sem_open()函式的行為標誌。

mode:檔案的許可權。

value:訊號量初始值。

獲取訊號量

sem_wait(sem_t *sem)

獲取訊號量,如果獲取不到則阻塞,或者其他異常錯誤發生。被用來阻塞當前執行緒直到訊號量sem值大於0,解除阻塞後將sem值減一,表明公共資源經使用後減少。它會等到訊號量為一個大於0的值才開始減一。

sem_trywait(sem_t *sem)

獲取訊號量,如果獲取不到,則返回錯誤碼。為sem_wait()的非阻塞版本。它直接將訊號量的值減一。

sem_timedwait(sem_t *sem, const struct timespec tsptr)

獲取訊號量,如果獲取不到,則最多等待指定的時間 tsptr,如果超時後仍然獲取不到,則返回錯誤碼。

釋放

sem_post(sem_t *sem, const struct timespec tsptr)

釋放指定的訊號量。等待在sem_wait()上的程序可以被喚醒。用來增加訊號量的值,當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中一個執行緒不在阻塞,選擇機制同樣由執行緒的排程策略決定的。

關閉有名訊號量

sem_close(sem_t *sem)                    

sem 為指向訊號量的指標。

刪除有名訊號量

sem_unlink(const char *name)                    

返回值:成功,返回0;出錯返回-1。

注:由於有名訊號量位於共享記憶體區,故它保護的資源也必須位於共享記憶體區。

4 辨析

下表是鎖的核心態和使用者態對應關係。其中pthread_cond_t是使用者態特有的,核心並沒有相對應的結構。

核心態

使用者態

spinlock_t

pthread_ spinlock_t

mutex_t

pthread_mutex_t、pthread_rwlock_t

struct semaphore

sem_t

rwlock_t

pthread_ spinlock_t

pthread_cond_t

核心態下各鎖的解析:

適合於中斷上下文情況下用的鎖

spinlock_t:自旋鎖、rwlock_t:讀寫自旋鎖

自旋鎖和讀寫自旋鎖都不會引起呼叫者進入休眠狀態,故可用於中斷上下文字中。

適合於程序下文的情況下用的鎖

mutex_t、semaphore

互斥鎖和訊號量,它們會導致呼叫者進入休眠狀態,因此只能在程序上下文使用。

自旋鎖、訊號量、互斥鎖以及讀寫鎖的區別於聯絡

聯絡:

1、讀寫自旋鎖和自旋鎖相相似,它們都不會引起呼叫者進入休眠狀態,可用於中斷上下文或給很小的程式碼段加鎖。

2、當訊號量的初值為1時,可以完成對資源的互斥訪問,其功能與互斥鎖相似。

3、互斥鎖、訊號量,它們都會會導致呼叫者進入休眠狀態。

區別:

讀寫自旋鎖與自旋鎖的區別:

1、讀寫自旋鎖在對資料結果讀的次數要比寫的次數多的情況下要比自旋鎖效能要高,讀寫鎖為讀鎖時,同一時間可以有多個執行緒獲得讀鎖,而自旋鎖同一時間只能有一個執行緒獲得鎖,其他執行緒進入休眠狀態。

互斥鎖與訊號量的區別:

1、互斥鎖用於執行緒的互斥,訊號量用於執行緒的同步。

2、訊號量的初始值可以為任意非負數,而互斥鎖的初始值只能為0/1

3、互斥量的加鎖和解鎖必須由同一執行緒分別對應使用,而訊號量可以由一個執行緒得到另一個執行緒釋放

使用者態下各鎖的解析:

適合於中斷上下文情況下用的鎖

pthread_ spinlock_t:自旋鎖

同一瞬間只能有一個執行緒能夠獲取鎖,其他執行緒在等待獲取鎖的過程中不會進入休眠狀態,而是在CPU上進入“自旋”等待。自旋鎖的效能很高,但是隻適合對很小的程式碼段加鎖(或短期持有的鎖),自旋鎖對CPU的佔用相對較高。

適合於程序下文的情況下用的鎖

pthread_mutex_t、sem_t、pthread_rwlock_t,

互斥鎖、訊號量以及讀寫鎖,它們會導致呼叫者進入休眠狀態,因此只能在程序上下文使用。

訊號量、互斥鎖以及讀寫鎖的區別於聯絡

聯絡:

1、讀寫鎖pthread_rwlock_t為互斥鎖pthread_mutex_t的衍生,在讀寫鎖為寫鎖時,其作用和互斥鎖一致。

2、互斥鎖是訊號量的特例,訊號量的初始值表示有多少個任務可以同時訪問共享資源,如果初始值為1,表示只有1個任務可以訪問,訊號量即與互斥鎖相似。

3、互斥鎖、訊號量以及讀寫鎖,它們都會會導致呼叫者進入休眠狀態。

區別:

互斥鎖與讀寫鎖的區別:

1、在對檔案進行讀操作時,互斥鎖同一時間只能有一個執行緒獲得鎖,其他執行緒在等待鎖的時候會進入休眠狀態,而讀寫鎖為讀鎖時,同一時間可以有多個執行緒獲得讀鎖,此時讀寫鎖的效能要比互斥鎖要高。

互斥鎖與訊號量的區別:

1、互斥鎖的加鎖和解鎖必須在同一執行緒裡對應使用,所以互斥鎖只能用於執行緒的互斥;訊號量可以由一個執行緒釋放,另一個執行緒得到,所以訊號量可以用於執行緒的同步。

2、訊號量通過初始值的設定可以決定有多少個任務可以同時訪問共享資源,所以訊號量的擴充套件性要好。而互斥鎖同一時間只能有一個執行緒獲得鎖,可擴充套件性差。