信號是E.W.Dijkstra在二十世紀六十年代末設計的一種編程架構。Dijkstra的模型與鐵路操做有關:假設某段鐵路是單線的,所以一次只容許一列火車經過。信號將用於同步經過該軌道的火車。火車在進入單一軌道以前必須等待信號燈變爲容許通行的狀態。火車進入軌道後,會改變信號狀態,防止其餘火車進入該軌道。火車離開這段軌道時,必須再次更改信號的狀態,以便容許其餘火車進入軌道。在計算機版本中,信號以簡單整數來表示。線程等待得到許能夠便繼續運行,而後發出信號,表示該線程已經經過針對信號執行P操做來繼續運行。線程必須等到信號的值爲正,而後才能經過將信號值減1來更改該值。完成此操做後,線程會執行V操做,即經過將信號值加1來更改該值。這些操做必須以原子方式執行,不能再將其劃分紅子操做,即,在這些子操做之間不能對信號執行其餘操做。在P操做中,信號值在減少以前必須爲正,從而確保生成的信號值不爲負,而且比該值減少以前小1。在P和V操做中,必須在沒有干擾的狀況下進行運算。若是針對同一信號同時執行兩個V操做,則實際結果是信號的新值比原來大2。對於大多數人來講,如同記住Dijkstra是荷蘭人同樣,記住P和V自己的含義並不重要。可是,真正學術的角度來講,P表明prolagen,這是由proberen te verlagen演變而來的杜撰詞,其意思是嘗試減少。V表明verhogen,其意思是增長。Dijkstra的技術說明EWD74中介紹了這些含義。sem_wait(3RT)和sem_post(3RT)分別與Dijkstra的P和V操做相對應。sem_trywait(3RT)是P操做的一種條件形式。若是調用線程不等待就不能減少信號的值,則該調用會當即返回一個非零值。有兩種基本信號:二進制信號和計數信號量。二進制信號的值只能是0或1,計數信號量可編程
以是任意非負值。二進制信號在邏輯上至關於一個互斥鎖。架構
不過,儘管不會強制,但互斥鎖應當僅由持有該鎖的線程來解除鎖定。由於不存在「持有信號的線程」這一律念,因此,任何線程均可以執行V或sem_post(3RT)操做。計數信號量與互斥鎖一塊兒使用時的功能幾乎與條件變量同樣強大。在許多狀況下,使用計數信號量實現的代碼比使用條件變量實現的代碼更爲簡單。可是,將互斥鎖用於條件變量時,會存在一個隱含的括號。該括號能夠清楚代表程序受保護的部分。對於信號則沒必要如此,可使用併發編程當中的go to對其進行調用。信號的功能強大,可是容易以非結構化的不肯定方式使用。併發
1 命名信號量和未命名信號量異步
POSIX信號能夠是未命名的,也能夠是命名的。未命名信號在進程內存中分配,並會進行初始化。未命名信號可能可供多個進程使用,具體取決於信號的分配和初始化的方式。未命名信號能夠是經過fork()繼承的專用信號,也能夠經過用來分配和映射這些信號的常規文件的訪問保護功能對其進行保護。命名信號相似於進程共享的信號,區別在於命名信號是使用路徑名而非pshared值引用的。命名信號能夠由多個進程共享。命名信號具備屬主用戶ID、組ID和保護模式。對於open、retrieve、close和remove命名信號,可使用如下函數:sem_open、sem_getvalue、sem_close和sem_unlink。經過使用sem_open,能夠建立一個命名信號,其名稱是在文件系統的名稱空間中定義的。函數
2 計數信號量概述post
從概念上來講,信號量是一個非負整數計數。信號量一般用來協調對資源的訪問,其中信號計數會初始化爲可用資源的數目。而後,線程在資源增長時會增長計數,在刪除資源時會減少計數,這些操做都以原子方式執行。若是信號計數變爲零,則代表已無可用資源。計數爲零時,嘗試減少信號的線程會被阻塞,直到計數大於零爲止。this
因爲信號無需由同一個線程來獲取和釋放,所以信號可用於異步事件通知,如用於信號處理程序中。同時,因爲信號包含狀態,所以能夠異步方式使用,而不用象條件變量那樣要求獲取互斥鎖。可是,信號的效率不如互斥鎖高。缺省狀況下,若是有多個線程正在等待信號,則解除阻塞的順序是不肯定的。信號在使用前必須先初始化,可是信號沒有屬性。線程
3 初始化信號量設計
使用sem_init(3RT)能夠將sem所指示的未命名信號變量初始化爲value。繼承
sem_init語法
int sem_init(sem_t *sem, int pshared, unsigned int value);
#include
sem_t sem;
int pshared;
int ret;
int value;
/* initialize a private semaphore */
pshared =0;
value =1;
ret = sem_init(&sem, pshared, value);
若是pshared的值爲零,則不能在進程之間共享信號。若是pshared的值不爲零,則能夠在進程之間共享信號。
注意:
(1)多個線程決不能初始化同一個信號。
(2)不得對其餘線程正在使用的信號從新初始化。
4 初始化進程內信號量
pshared爲0時,信號只能由該進程內的全部線程使用。
#include
sem_t sem;
int ret;
int count = 4;
/* to be used within this process only */
ret = sem_init(&sem, 0, count);
5 初始化進程間信號量
pshared不爲零時,信號能夠由其餘進程共享。
#include
sem_t sem;
int ret;
int count = 4;
/* to be shared among processes */
ret = sem_init(&sem, 1, count);
6 sem_init返回值
sem_init()在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。
EINVAL
描述:參數值超過了SEM_VALUE_MAX。
ENOSPC
描述:初始化信號所需的資源已經用完。到達信號的SEM_NSEMS_MAX限制。
ENOSYS
描述:系統不支持sem_init()函數。
EPERM
描述:進程缺乏初始化信號所需的適當權限。
7 增長信號
sem_post語法
int sem_post(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_post(&sem); /* semaphore is posted */
若是全部線程均基於信號阻塞,則會對其中一個線程解除阻塞。
sem_post返回值
sem_post()在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下狀況,該函數將失敗並返回對應的值。
EINVAL
描述: sem所指示的地址非法。
8 基於信號計數進行阻塞
使用sem_wait(3RT)能夠阻塞調用線程,直到sem所指示的信號計數大於零爲止,以後以原
子方式減少計數。
sem_wait語法
int sem_wait(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_wait(&sem); /* wait for semaphore */
sem_wait返回值
sem_wait()在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。
EINVAL
描述: sem所指示的地址非法。
EINTR
描述:此函數已被信號中斷。
9 減少信號計數
使用sem_trywait(3RT)能夠在計數大於零時,嘗試以原子方式減少sem所指示的信號計數。
sem_trywait語法
int sem_trywait(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_trywait(&sem); /* try to wait for semaphore*/
此函數是sem_wait()的非阻塞版本。sem_trywait()在失敗時會當即返回。
sem_trywait返回值
sem_trywait()在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。
EINVAL
描述: sem所指示的地址非法。
EINTR
描述:此函數已被信號中斷。
EAGAIN
描述:信號已爲鎖定狀態,所以該信號不能經過sem_trywait()操做當即鎖定。
10 銷燬信號狀態
使用sem_destroy(3RT)能夠銷燬與sem所指示的未命名信號相關聯的任何狀態。
sem_destroy語法
int sem_destroy(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_destroy(&sem); /* the semaphore is destroyed */
sem_destroy返回值
sem_destroy()在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現以
下狀況,該函數將失敗並返回對應的值。
EINVAL
描述: sem所指示的地址非法。