一. 信號燈簡介linux
信號燈與其餘進程間通訊方式不大相同,它主要提供對進程間共享資源訪問控制機制。api
至關於內存中的標誌,進程能夠根據它斷定是否可以訪問某些共享資源,同時,進程數組
也能夠修改該標誌。除了用於訪問控制外,還可用於進程同步。安全
信號燈有如下兩種類型:函數
二值信號燈:最簡單的信號燈形式,信號燈的值只能取0或1,相似於互斥鎖。 post
注:二值信號燈可以實現互斥鎖的功能,但二者的關注內容不一樣。信號燈強調共享資源,測試
只要共享資源可用,其餘進程一樣能夠修改信號燈的值;互斥鎖更強調進程,佔用資源spa
的進程使用完資源後,必須由進程自己來解鎖。線程
計算信號燈:信號燈的值能夠取任意非負值(固然受內核自己的約束)。設計
系統V信號燈是隨內核持續的,只有在內核重起或者顯示刪除一個信號燈集時,該信號
燈集纔會真正被刪除。
二. 信號燈的基本操做
對信號燈的操做無非有下面三種類型:
一、打開或建立信號燈
二、信號燈值操做
linux能夠增長或減少信號燈的值,相應於對共享資源的釋放和佔有。具體參見後面的
semop系統調用。
三、得到或設置信號燈屬性:
系統中的每個信號燈集都對應一個struct sem_array結構,該結構記錄了信號燈集
的各類信息,存在於系統空間。爲了設置、得到該信號燈集的各類信息及屬性,在用戶
空間有一個重要的聯合結構與之對應,即union semun。
3、系統V信號燈API
系統V消息隊列API只有三個,使用時須要包括幾個頭文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1)int semget(key_t key, int nsems, int semflg)
參數key是一個鍵值,由ftok得到,惟一標識一個信號燈集.
參數nsems指定信號燈集包含信號燈的數目;
semflg參數是一些標誌位。
該調用返回與健值key相對應的信號燈集id
調用返回:成功返回信號燈集描述字,不然返回-1。
2)int semop(int semid, struct sembuf *sops, unsigned nsops);
semid是信號燈集ID,sops數組的每個sembuf結構都刻畫一個在特定信號燈上的操做。
nsops爲sops數組的大小。 sembuf結構以下:
struct sembuf {
unsigned short sem_num;/* semaphore index in array */
shortsem_op;/* semaphore operation */
shortsem_flg;/* operation flags */
};
sem_num對應集合中的信號燈,0對應第一個信號燈, 以此類推...
sem_flg可取IPC_NOWAIT以及SEM_UNDO兩個標誌。若是設置了SEM_UNDO標誌,
那麼在進程結束時,相應的操做將被取消,這是比較重要的一個標誌位。若是設置了該標
志位,那麼在進程沒有釋放共享資源就退出時,內核將代爲釋放。若是爲一個信號燈設置
了該標誌,內核都要分配一個sem_undo結構來記錄它,爲的是確保之後資源可以安全釋
放。事實上,若是進程退出了,那麼它所佔用就釋放了,但信號燈值卻沒有改變,此時,
信號燈值反映的已經不是資源佔有的實際狀況,在這種狀況下,問題的解決就靠內核來完
成。這有點像殭屍進程,進程雖然退出了,資源也都釋放了,但內核進程表中仍然有它的
記錄,此時就須要父進程調用waitpid來解決問題了。
sem_op的值大於0,等於0以及小於0肯定了對sem_num指定的信號燈進行的三種操做。
這裏須要強調的是semop能夠同時操做多個信號燈,在實際應用中,對應多種資源的申請
或釋放。semop保證操做的原子性,這一點尤其重要。尤爲對於多種資源的申請來講,要
麼一次性得到全部資源,要麼放棄申請,要麼在不佔有任何資源狀況下繼續等待,這樣,
一方面避免了資源的浪費;另外一方面,避免了進程之間因爲申請共享資源形成死鎖。
也許從實際含義上更好理解這些操做:信號燈的當前值記錄相應資源目前可用數目;sem_op>0對應相應進程要釋放sem_op數目的共享資源;sem_op=0能夠用於對共享資
源是否已用完的測試;sem_op<0至關於進程要申請-sem_op個共享資源。再聯想操做的
原子性,更不難理解該系統調用什麼時候正常返回,什麼時候睡眠等待。
調用返回:成功返回0,不然返回-1。
3) int semctl(int semid,int semnum,int cmd,union semun arg)
該系統調用實現對信號燈的各類控制操做,參數semid指定信號燈集,參數cmd指定具體的
操做類型;參數semnum指定對哪一個信號燈操做,只對幾個特殊的cmd操做有意義;arg用
於設置或返回信號燈信息。
該系統調用詳細信息請參見其手冊頁,這裏只給出參數cmd所能指定的操做。
IPC_STAT獲取信號燈信息,信息由arg.buf返回;
IPC_SET設置信號燈信息,待設置信息保存在arg.buf中.
GETALL返回全部信號燈的值,結果保存在arg.array中,參數sennum被忽略;
GETNCNT返回等待semnum所表明信號燈的值增長的進程數,至關於目前有多少
進程在等待semnum表明的信號燈所表明的共享資源;
GETPID返回最後一個對semnum所表明信號燈執行semop操做的進程ID;
GETVAL返回semnum所表明信號燈的值;
GETZCNT返回等待semnum所表明信號燈的值變成0的進程數;
SETALL經過arg.array更新全部信號燈的值;同時,更新與本信號集相關的
semid_ds結構的sem_ctime成員;
SETVAL設置semnum所表明信號燈的值爲arg.val;
調用返回:調用失敗返回-1,成功返回與cmd相關:
Cmdreturn value
GETNCNTSemncnt
GETPIDSempid
GETVALSemval
GETZCNTSemzcnt
semctl函數使用到的結構體:
union semun {
int val;/* value for SETVAL */
struct semid_ds *buf;/* buffer for IPC_STAT & IPC_SET */
unsigned short *array;/* array for GETALL & SETALL */
struct seminfo *__buf;/* buffer for IPC_INFO */ //test!!
void *__pad;
};
struct seminfo {
int semmap;
int semmni;
int semmns;
int semmnu;
int semmsl;
int semopm;
int semume;
int semusz;
int semvmx;
int semaem;
};
4、範例
這個範例使用信號燈來同步共享內存的操做, 程序建立一塊共享內存, 而後父子進程共同
修改共享內存. 父子進程採用信號燈來同步操做.
Systm V | POSIX |
semctl() | sem_getvalue() |
semget() | sem_post() |
semop() | sem_timedwait() |
sem_trywait() | |
sem_wait() | |
sem_destroy() | |
sem_init() | |
sem_close() | |
sem_open() | |
sem_unlink() |
Posix的無名信號量通常用於線程同步, 無名信號量是進程持續的, 無名信號量的api爲
sem_init
sem_destroy
下面一個範例使用Posix的有名信號量來同步父子進程的共享內存操做: