信號量

信號量

信號量(Semaphore),有時被稱為信號燈,是在多執行緒環境下使用的一種設施,是可以用來保證兩個或多個關鍵代碼段不被並發調用。在進入一個關鍵代碼段之前,執行緒必須獲取一個信號量;一旦該關鍵代碼段完成了,那么該執行緒必須釋放信號量。其它想進入該關鍵代碼段的執行緒必須等待直到第一個執行緒釋放信號量。為了完成這個過程,需要創建一個信號量VI,然後將Acquire Semaphore VI以及Release Semaphore VI分別放置在每個關鍵代碼段的首末端。確認這些信號量VI引用的是初始創建的信號量。

描述

以一個停車場的運作為例。簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許其中三輛直接進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,打開車攔,放入外面的一輛進去,如果又離開兩輛,則又可以放入兩輛,如此往復。

在這個停車場系統中,車位是公共資源,每輛車好比一個執行緒,看門人起的就是信號量的作用。

分類

整型信號量(integer semaphore):信號量是整數

記錄型信號量(record semaphore):每個信號量s除一個整數值s.value(計數)外,還有一個進程等待佇列s.L,其中是阻塞在該信號量的各個進程的標識

二進制信號量(binary semaphore):只允許信號量取0或1值

每個信號量至少須記錄兩個信息:信號量的值和等待該信號量的進程佇列。它的類型定義如下:(用類PASCAL語言表述)

semaphore = record

value: integer;

queue: ^PCB;

end;

其中PCB是進程控制塊,是作業系統為每個進程建立的數據結構。

s.value>=0時,s.queue為空;

s.value<0時,s.value的絕對值為s.queue中等待進程的個數;

特性

抽象的來講,信號量的特性如下:信號量是一個非負整數(車位數),所有通過它的執行緒/進程(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的執行緒都將處於等待狀態。在信號量上我們定義兩種操作: Wait(等待) 和 Release(釋放)。當一個執行緒調用Wait操作時,它要么得到資源然後將信號量減一,要么一直等下去(指放入阻塞佇列),直到信號量大於等於一時。Release(釋放)實際上是在信號量上執行加操作,對應於車輛離開停車場,該操作之所以叫做“釋放”是因為釋放了由信號量守護的資源。

操作方式

對信號量有4種操作(#include<semaphore.h>):

1. 初始化(initialize),也叫做建立(create) int sem_init(sem_t *sem, int pshared, unsigned int value);

2. 等信號(wait),也可叫做掛起(suspend)int sem_wait(sem_t *sem);

3. 給信號(signal)或發信號(post) int sem_post(sem_t *sem);

4.清理(destroy) int sem_destory(sem_t *sem);

創建

同共享記憶體一樣,系統中同樣需要為信號量集定製一系列專有的操作函式(semget,semctl等)。系統命令ipcs可查看當前的系統IPC的狀態,在命令後使用-s參數。使用函式semget可以創建或者獲得一個信號量集ID,函式原型如下:

#include <sys/shm.h>

int semget( key_t key, int nsems, int flag);

函式中參數key用來變換成一個標識符,每一個IPC對象與一個key相對應。當新建一個共享記憶體段時,使用參數flag的相應許可權位對ipc_perm結構中的mode域賦值,對相應信號量集的shmid_ds初始化的值如表1所示。

shmid_ds結構初始化值表

ipc_perm結構數據 初 值 ipc_perm結構數據 初 值
Sem_otime 0 Sem_nsems Nsems
Sem_ctime 系統當前值   

參數nsems是一個大於等於0的值,用於指明該信號量集中可用資源數(在創建一個信號量時)。當打開一個已存在的信號量集時該參數值為0。函式執行成功,則返回信號量集的標識符(一個大於等於0的整數),失敗,則返回–1。函式semop用以操作一個信號量集,函式原型如下:

#include <sys/sem.h>

int semop( int semid, struct sembuf semoparray[], size_t nops );

函式中參數semid是一個通過semget函式返回的一個信號量標識符,參數nops標明了參數semoparray所指向數組中的元素個數。參數semoparray是一個struct sembuf結構類型的數組指針,結構sembuf來說明所要執行的操作,其定義如下:

struct sembuf{

unsigned short sem_num;

short sem_op;

short sem_flg;

}

在sembuf結構中,sem_num是相對應的信號量集中的某一個資源,所以其值是一個從0到相應的信號量集的資源總數(ipc_perm.sem_nsems)之間的整數。sem_op指明所要執行的操作,sem_flg說明函式semop的行為。sem_op的值是一個整數,如表2所示,列出了詳細sem_op的值及所對應的操作。

sem_op值詳解

Sem_op 操 作
正數 釋放相應的資源數,將sem_op的值加到信號量的值上
0 進程阻塞直到信號量的相應值為0,當信號量已經為0,函式立即返回。如果信號量的值不為0,則依據sem_flg的IPC_NOWAIT位決定函式動作。sem_flg指定IPC_NOWAIT,則semop函式出錯返回EAGAIN。sem_flg沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,然後進程掛起直到下述情況發生。信號量值為0,將信號量的semzcnt的值減1,函式semop成功返回;此信號量被刪除(只有超級用戶或創建用戶進程擁有此許可權),函式smeop出錯返回EIDRM;進程捕捉到信號,並從信號處理函式返回,在此情況將此信號量的semncnt值減1,函式semop出錯返回EINTR
負數 請求sem_op的絕對值的資源。如果相應的資源數可以滿足請求,則將該信號量的值減去sem_op的絕對值,函式成功返回。當相應的資源數不能滿足請求時,這個操作與sem_flg有關。sem_flg指定IPC_NOWAIT,則semop函式出錯返回EAGAIN。sem_flg沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,然後進程掛起直到下述情況發生:當相應的資源數可以滿足請求,該信號的值減去sem_op的絕對值。成功返回;此信號量被刪除(只有超級用戶或創建用戶進程擁有此許可權),函式smeop出錯返回EIDRM:進程捕捉到信號,並從信號處理函式返回,在此情況將此信號量的semncnt值減1,函式semop出錯返回EINTR

基本流程

下面實例演示了關於信號量操作的基本流程。程式中使用semget函式創建一個信號量集,並使用semop函式在這個信號集上執行了一次資源釋放操作。並在shell中使用命令查看系統IPC的狀態。

(1)在vi編輯器中編輯該程式。

程式清單14-10 create_sem.c 使用semget函式創建一個信號量

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#include <stdio.h>

#include <stdlib.h>

int main( void )

{

int sem_id;

int nsems = 1;

int flags = 0666;

struct sembuf buf;

sem_id = semget(IPC_PRIVATE, nsems, flags); /*創建一個新的信號量集*/

if ( sem_id < 0 ){

perror( "semget ") ;

exit (1 );

}

/*輸出相應的信號量集標識符*/

printf ( "successfully created a semaphore : %d\n", sem_id );

buf.sem_num = 0; /*定義一個信號量操作*/

buf.sem_op = 1; /*執行釋放資源操作*/

buf.sem_flg = IPC_NOWAIT; /*定義semop函式的行為*/

if ( (semop( sem_id, &buf, nsems) ) < 0) { /*執行操作*/

perror ( "semop");

exit (1 );

}

system ( "ipcs -s " ); /*查看系統IPC狀態*/

exit ( 0 );

}

(2)在vmware中編譯該程式如下:

gcc -o a.o testc_semaphore.c

(3)在shell中運行該程式如下:

./a3.o

successfully created a semaphore : 0

------ Semaphore Arrays --------
key semid owner perms nsems
0x00000000 0 zcr 666 1
在上面程式中,用semget函式創建了一個信號量集,定義信號量集的資源數為1,接下來使用semop函式進行資源釋放操作。在程式的最後使用shell命令ipcs來查看系統IPC的狀態。

%注意:命令ipcs參數-s標識查看系統IPC的信號量集狀態。

發展史

1965年,荷蘭學者Edsger Dijkstra提出的信號量(Semaphores)機制是一種卓有成效的進程同步工具,在長期廣泛的套用中,信號量機製得到了極大的發展,它從整型信號量經記錄型信號量,進而發展成為“信號量集機制”,現在信號量機制已經被廣泛的套用到單處理機和多處理機系統以及計算機網路中。

相關詞條

相關搜尋

熱門詞條

聯絡我們