标簽:信号量(3)Linux(1561)
8.4.1 信号量概述
在多任務作業系統環境下,多個程序會同時運作,并且一些程序之間可能存在一定的關聯。多個程序可能為了完成同一個任務會互相協作,這樣形成程序之間的同步關系。而且在不同程序之間,為了争奪有限的系統資源(硬體或軟體資源)會進入競争狀态,這就是程序之間的互斥關系。
程序之間的互斥與同步關系存在的根源在于臨界資源。臨界資源是在同一個時刻隻允許有限個(通常隻有一個)程序可以通路(讀)或修改(寫)的資源,通常包括硬體資源(處理器、記憶體、存儲器以及其他外圍裝置等)和軟體資源(共享代碼段,共享結構和變量等)。通路臨界資源的代碼叫做臨界區,臨界區本身也會成為臨界資源。
信号量是用來解決程序之間的同步與互斥問題的一種程序之間通信機制,包括一個稱為信号量的變量和在該信号量下等待資源的程序等待隊列,以及對信号量進行的兩個原子操作(PV操作)。其中信号量對應于某一種資源,取一個非負的整型值。信号量值指的是目前可用的該資源的數量,若它等于0則意味着目前沒有可用的資源。PV原子操作的具體定義如下:
P操作:如果有可用的資源(信号量值》0),則占用一個資源(給信号量值減去一,進入臨界區代碼);如果沒有可用的資源(信号量值等于0),則被阻塞到,直到系統将資源配置設定給該程序(進入等待隊列,一直等到資源輪到該程序)。
V操作:如果在該信号量的等待隊列中有程序在等待資源,則喚醒一個阻塞程序。如果沒有程序等待它,則釋放一個資源(給信号量值加一)。
使用信号量通路臨界區的僞代碼所下所示:
{
INIT_VAL(S);
非臨界區;
P(S);
臨界區(使用資源R);
V(S);
非臨界區;
}
最簡單的信号量是隻能取0和1兩種值,這種信号量被叫做二維信号量。在本節中,主要讨論二維信号量。二維信号量的應用比較容易地擴充到使用多元信号量的情況。
8.4.2 信号量的應用
1.函數說明
在Linux系統中,使用信号量通常分為以下幾個步驟。
(1)建立信号量或獲得在系統已存在的信号量,此時需要調用semget()函數。不同程序通過使用同一個信号量鍵值來獲得同一個信号量。
(2)初始化信号量,此時使用semctl()函數的SETVAL操作。當使用二維信号量時,通常将信号量初始化為1。
(3)進行信号量的PV操作,此時調用semop()函數。這一步是實作程序之間的同步和互斥的核心工作部分。
(4)如果不需要信号量,則從系統中删除它,此時使用semclt()函數的IPC_RMID操作。此時需要注意,在程式中不應該出現對已經被删除的信号量的操作。
2.函數格式
表8.17列舉了semget()函數的文法要點。
表8.17 semget()函數文法要點
所需頭檔案#include 《sys/types.h》
#include 《sys/ipc.h》
#include 《sys/sem.h》
函數原型int semget(key_t key, int nsems, int semflg)
函數傳入值key:信号量的鍵值,多個程序可以通過它通路同一個信号量,其中有個特殊值IPC_PRIVATE。它用于建立目前程序的私有信号量
nsems:需要建立的信号量數目,通常取值為1
semflg:同open()函數的權限位,也可以用八進制表示法,其中使用IPC_CREAT标志建立新的信号量,即使該信号量已經存在(具有同一個鍵值的信号量已在系統中存在),也不會出錯。如果同時使用IPC_EXCL标志可以建立一個新的唯一的信号量,此時如果該信号量已經存在,該函數會傳回出錯
函數傳回值成功:信号量辨別符,在信号量的其他函數中都會使用該值
出錯:-1
表8.18列舉了semctl()函數的文法要點。
表8.18 semctl()函數文法要點
所需頭檔案#include 《sys/types.h》
#include 《sys/ipc.h》
#include 《sys/sem.h》
函數原型int semctl(int semid, int semnum, int cmd, union semun arg)
函數傳入值semid:semget()函數傳回的信号量辨別符
semnum:信号量編号,當使用信号量集時才會被用到。通常取值為0,就是使用單個信号量(也是第一個信号量)
cmd:指定對信号量的各種操作,當使用單個信号量(而不是信号量集)時,常用的有以下幾種:
IPC_STAT:獲得該信号量(或者信号量集合)的semid_ds結構,并存放在由第4個參數arg的buf指向的semid_ds結構中。semid_ds是在系統中描述信号量的資料結構。
IPC_SETVAL:将信号量值設定為arg的val值
IPC_GETVAL:傳回信号量的目前值
IPC_RMID:從系統中,删除信号量(或者信号量集)
arg:是union semnn結構,該結構可能在某些系統中并不給出定義,此時必須由程式員自己定義
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}
函數傳回值成功:根據cmd值的不同而傳回不同的值
IPC_STAT、IPC_SETVAL、IPC_RMID:傳回0
IPC_GETVAL:傳回信号量的目前值
出錯:-1
表8.19列舉了semop()函數的文法要點。
表8.19 semop()函數文法要點
所需頭檔案#include 《sys/types.h》
#include 《sys/ipc.h》
#include 《sys/sem.h》
函數原型int semop(int semid, struct sembuf *sops, size_t nsops)
函數傳入值semid:semget()函數傳回的信号量辨別符
sops:指向信号量操作數組,一個數組包括以下成員:
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
nsops:操作數組sops中的操作個數(元素數目),通常取值為1(一個操作)
函數傳回值成功:信号量辨別符,在信号量的其他函數中都會使用該值
出錯:-1