久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

(轉(zhuǎn)載)關(guān)于信號(hào)量與線程互斥鎖的區(qū)別與實(shí)現(xiàn)

 hehffyy 2011-11-30
之前一直沒(méi)有怎么關(guān)注過(guò)這個(gè)問(wèn)題,前些日子在面試一家公司的時(shí)候,,面試官提到了pthread_cond_wait/pthread_cond_signal的實(shí)現(xiàn),,當(dāng)時(shí)答的不是很好,,回來(lái)就查了nptl的代碼,。前天,,水木上又有人問(wèn)到了信號(hào)量和互斥鎖的問(wèn)題,,我想還是對(duì)它們的區(qū)別與實(shí)現(xiàn)總結(jié)一下,。 

首先了解一些信號(hào)量和線程互斥鎖的語(yǔ)義上的區(qū)別: 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 

援引CU上一篇帖子的內(nèi)容: 
“信號(hào)量用在多線程多任務(wù)同步的,一個(gè)線程完成了某一個(gè)動(dòng)作就通過(guò)信號(hào)量告訴別的線程,,別的線程再進(jìn)行某些動(dòng)作(大家都在sem_wait的時(shí)候,,就阻塞在那里)。而互斥鎖是用在多線程多任務(wù)互斥的,,一個(gè)線程占用了某一個(gè)資源,,那么別的線程就無(wú)法訪問(wèn),直到這個(gè)線程unlock,,其他的線程才開始可以利用這個(gè)資源,。比如對(duì)全局變量的訪問(wèn),有時(shí)要加鎖,,操作完了,,在解鎖。有的時(shí)候鎖和信號(hào)量會(huì)同時(shí)使用的” 
也就是說(shuō),,信號(hào)量不一定是鎖定某一個(gè)資源,,而是流程上的概念,比如:有A,B兩個(gè)線程,,B線程要等A線程完成某一任務(wù)以后再進(jìn)行自己下面的步驟,,這個(gè)任務(wù)并不一定是鎖定某一資源,還可以是進(jìn)行一些計(jì)算或者數(shù)據(jù)處理之類,。而線程互斥量則是“鎖住某一資源”的概念,,在鎖定期間內(nèi),其他線程無(wú)法對(duì)被保護(hù)的數(shù)據(jù)進(jìn)行操作,。在有些情況下兩者可以互換,。 

兩者之間的區(qū)別: 

作用域 
信號(hào)量: 進(jìn)程間或線程間(linux僅線程間) 
互斥鎖: 線程間 

上鎖時(shí) 
信號(hào)量: 只要信號(hào)量的value大于0,其他線程就可以sem_wait成功,,成功后信號(hào)量的value減一,。若value值不大于0,則sem_wait阻塞,,直到sem_post釋放后value值加一,。一句話,信號(hào)量的value>=0,。 
互斥鎖: 只要被鎖住,,其他任何線程都不可以訪問(wèn)被保護(hù)的資源,。如果沒(méi)有鎖,獲得資源成功,,否則進(jìn)行阻塞等待資源可用,。一句話,線程互斥鎖的vlaue可以為負(fù)數(shù),。 

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

接下來(lái),,我們需要分析一下信號(hào)量和線程互斥鎖的實(shí)現(xiàn)機(jī)制。 

在Linux下,,信號(hào)量和線程互斥鎖的實(shí)現(xiàn)都是通過(guò)futex系統(tǒng)調(diào)用,。 

futex(快速用戶區(qū)互斥的簡(jiǎn)稱)是一個(gè)在Linux上實(shí)現(xiàn)鎖定和構(gòu)建高級(jí)抽象鎖如信號(hào)量和POSIX互斥的基本工具。它們第一次出現(xiàn)在內(nèi)核開發(fā)的2.5.7版,;其語(yǔ)義在2.5.40固定下來(lái),,然后在2.6.x系列穩(wěn)定版內(nèi)核中出現(xiàn)。 

Futex 是fast userspace mutex的縮寫,,意思是快速用戶空間互斥體,。Linux內(nèi)核把它們作為快速的用戶空間的鎖和信號(hào)量的預(yù)制構(gòu)件提供給開發(fā)者。Futex非?;A(chǔ),,借助其自身的優(yōu)異性能,構(gòu)建更高級(jí)別的鎖的抽象,,如POSIX互斥體,。大多數(shù)程序員并不需要直接使用Futex,它一般用來(lái)實(shí)現(xiàn)像NPTL這樣的系統(tǒng)庫(kù),。 

Futex 由一塊能夠被多個(gè)進(jìn)程共享的內(nèi)存空間(一個(gè)對(duì)齊后的整型變量)組成,;這個(gè)整型變量的值能夠通過(guò)匯編語(yǔ)言調(diào)用CPU提供的原子操作指令來(lái)增加或減少,并且一個(gè)進(jìn)程可以等待直到那個(gè)值變成正數(shù),。Futex 的操作幾乎全部在應(yīng)用程序空間完成,;只有當(dāng)操作結(jié)果不一致從而需要仲裁時(shí),才需要進(jìn)入操作系統(tǒng)內(nèi)核空間執(zhí)行,。這種機(jī)制允許使用 futex 的鎖定原語(yǔ)有非常高的執(zhí)行效率:由于絕大多數(shù)的操作并不需要在多個(gè)進(jìn)程之間進(jìn)行仲裁,,所以絕大多數(shù)操作都可以在應(yīng)用程序空間執(zhí)行,而不需要使用(相對(duì)高代價(jià)的)內(nèi)核系統(tǒng)調(diào)用,。 

---------------------------------------------------------------- 
插播一段關(guān)于x86原子操作指令的說(shuō)明: 

cmpxchg 比較交換指令,,其語(yǔ)義為: 
Java代碼  收藏代碼
  1. int CompareAndExchange(int *ptr, int old, int new)  
  2. {  
  3. int actual = *ptr;  
  4. if (actual == old)  
  5. *ptr = new;  
  6. return actual;  
  7. }  
  8.    


Intel白皮書上的說(shuō)明如下: 
(* Accumulator = AL, AX, EAX, or RAX depending on whether a byte, word, doubleword, or 
quadword comparison is being performed *) 
IF accumulator = DEST 
    THEN 
        ZF ← 1; 
        DEST ← SRC; 
    ELSE 
        ZF ← 0; 
        accumulator ← DEST; 
FI; 

使用此原子操作可以實(shí)現(xiàn)自旋鎖,之前有一篇文章中描述了實(shí)現(xiàn): 
Java代碼  收藏代碼
  1. void lock(lock_t *lock) {  
  2. while (CompareAndExchange(&lock->flag, 01) == 1)  
  3. // spin  
  4. }  
  5. void unlock(lock_t *lock) {  
  6. lock->flag = 0;  
  7. }  



關(guān)于smp下的原子操作的一些說(shuō)明: 
  原子操作是不可分割的,,在執(zhí)行完畢不會(huì)被任何其它任務(wù)或事件中斷,。在單處理器系統(tǒng)(UniProcessor)中,能夠在單條指令中完成的操作都可以認(rèn)為是" 原子操作",因?yàn)橹袛嘀荒馨l(fā)生于指令之間,。這也是某些CPU指令系統(tǒng)中引入了test_and_set,、test_and_clear等指令用于臨界資源互斥的原因。在對(duì)稱多處理器(Symmetric Multi-Processor)結(jié)構(gòu)中就不同了,,由于系統(tǒng)中有多個(gè)處理器在獨(dú)立地運(yùn)行,,即使能在單條指令中完成的操作也有可能受到干擾。 
  在x86 平臺(tái)上,,CPU提供了在指令執(zhí)行期間對(duì)總線加鎖的手段,。CPU芯片上有一條引線#HLOCK pin,如果匯編語(yǔ)言的程序中在一條指令前面加上前綴"LOCK",,經(jīng)過(guò)匯編以后的機(jī)器代碼就使CPU在執(zhí)行這條指令的時(shí)候把#HLOCK pin的電位拉低,,持續(xù)到這條指令結(jié)束時(shí)放開,從而把總線鎖住,,這樣同一總線上別的CPU就暫時(shí)不能通過(guò)總線訪問(wèn)內(nèi)存了,保證了這條指令在多處理器環(huán)境中的原子性,。 
  當(dāng)然,,并不是所有的指令前面都可以加lock前綴的,只有ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG,DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, 和 XCHG指令前面可以加lock指令,,實(shí)現(xiàn)原子操作,。 
---------------------------------------------------------------- 

廣告回來(lái)了,我們繼續(xù),。 

futex保存在用戶空間的共享內(nèi)存中,,并且通過(guò)原子操作進(jìn)行操作。在大部分情況下,,資源不存在爭(zhēng)用的情況下,,進(jìn)程或者線程可以立刻獲得資源成功,實(shí)際上就沒(méi)有必要調(diào)用系統(tǒng)調(diào)用,,陷入內(nèi)核了,。實(shí)際上,futex的作用就在于減少系統(tǒng)調(diào)用的次數(shù),,來(lái)提高系統(tǒng)的性能,。 

線程互斥鎖pthread_mutex_t的實(shí)現(xiàn)原理: 
Java代碼  收藏代碼
  1. pthread_mutex_lock:  
  2. atomic_dec(pthread_mutex_t.value);  
  3. if(pthread_mutex_t.value!=0)  
  4.    futex(WAIT)  
  5. else  
  6.    success  
  7.   
  8. pthread_mutex_unlock:  
  9. atomic_inc(pthread_mutex_t.value);  
  10. if(pthread_mutex_t.value!=1)  
  11.    futex(WAKEUP)  
  12. else  
  13.    success  


信號(hào)量sem_t的實(shí)現(xiàn)原理(直接從glibc/nptl/DESIGN-sem.txt中摘的): 
Java代碼  收藏代碼
  1. sem_wait(sem_t *sem)  
  2. {  
  3.   for (;;) {  
  4.   
  5.     if (atomic_decrement_if_positive(sem->count))  
  6.       break;  
  7.   
  8.     futex_wait(&sem->count, 0)  
  9.   }  
  10. }  
  11.   
  12. sem_post(sem_t *sem)  
  13. {  
  14.   n = atomic_increment(sem->count);  
  15.   // Pass the new value of sem->count  
  16.   futex_wake(&sem->count, n + 1);  


對(duì)比,pthread_mutex_unlock()和sem_post()的實(shí)現(xiàn),,我們發(fā)現(xiàn)一個(gè)不同點(diǎn),,sem_post()無(wú)論如何都會(huì)調(diào)用futex_wake(),進(jìn)行系統(tǒng)調(diào)用,。但是pthread_mutex_unlock()卻符合futex的初衷,,只有在需要仲裁的時(shí)候才調(diào)用futex_wake()。那么什么是仲裁條件呢,? 

前面說(shuō)過(guò)信號(hào)量和線程互斥鎖語(yǔ)義上的區(qū)別在于信號(hào)量的value>=0,,而線程互斥鎖的value可以為負(fù)數(shù),。 
對(duì)于lock操作,這兩個(gè)倒是沒(méi)有多少差別,。信號(hào)量只要value>0就可以獲得資源,,線程互斥鎖需要value=1。 
但是對(duì)于unlock操作,,這兩個(gè)就有一些差別了,。信號(hào)量和線程互斥鎖,都會(huì)增加對(duì)應(yīng)的value,。如果加1后,,value為1,對(duì)于線程互斥鎖來(lái)講,,實(shí)際上表明資源可用,,并且之前沒(méi)有其他的線程在等待這個(gè)資源;否則說(shuō)明還有其他線程在等待這個(gè)資源,,需要調(diào)用futex系統(tǒng)調(diào)用喚醒它們,。但是對(duì)于信號(hào)量,由于value必須>=0,。那么加1后,,即使value為1,也無(wú)法判定現(xiàn)在沒(méi)有其他的進(jìn)程或線程正在等待資源,,所以必須調(diào)用futex系統(tǒng)調(diào)用,。例如: 
Java代碼  收藏代碼
  1. #include <stdio.h>  
  2. #include <semaphore.h>  
  3. #include <pthread.h>  
  4.   
  5. sem_t sem_a;  
  6. void *task1();  
  7.   
  8. int main(void)  
  9. {  
  10.  int ret=0;  
  11.  pthread_t thrd1;  
  12.  pthread_t thrd2;  
  13.  sem_init(&sem_a,0,1);  
  14.  ret=pthread_create(&thrd1,NULL,task1,NULL); //創(chuàng)建子線程  
  15.  ret=pthread_create(&thrd2,NULL,task1,NULL); //創(chuàng)建子線程  
  16.  pthread_join(thrd1,NULL); //等待子線程結(jié)束  
  17.  pthread_join(thrd2,NULL); //等待子線程結(jié)束  
  18. }  
  19.   
  20. void *task1()  
  21. {  
  22.   int sval = 0;  
  23.   sem_wait(&sem_a); //持有信號(hào)量  
  24.   sleep(5); //do_nothing  
  25.   sem_getvalue(&sem_a,&sval);  
  26.   printf("sem value = %d\n",sval);  
  27.   sem_post(&sem_a); //釋放信號(hào)量  
  28. }   
上面sem的value初始化為1,但是有兩個(gè)線程爭(zhēng)用資源,。那么第一個(gè)線程獲得資源成功,,當(dāng)它unlock的時(shí)候,sem的value變?yōu)?,。但是,,這個(gè)時(shí)候,實(shí)際上還有一個(gè)線程在等待資源,。因此,,必須要進(jìn)行futex_wake()系統(tǒng)調(diào)用,喚醒等待資源的線程,。 

感興趣的同學(xué)可以使用strace跟蹤一下,,進(jìn)行驗(yàn)證。要注意忽略程序運(yùn)行初始化的那個(gè)futex_wake ;-) 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多