一 信號量(信號燈)概念:
信號量提供一種訪問機制,讓一個臨界區同一時間只有一個進程在訪問他,也就是說信號量用來協調進程對共享資源的訪問的。
信號量是一個特殊的變量,程序對其訪問都是原子操作,只允許對他進行等待(P)和發送信息(V)的操作。最簡單的信號量只能取0和1的變量,這也是信號量最常見的一種形式,叫做二進制信號量。可以取多個正整數的信號量稱爲信號量。
信號量與其他進程間通信方式不大相同,主要用途是保護臨界資源。進程可以根據他判斷是否能夠訪問某些資源。
當進程A要獲取臨界資源S時,首先要獲取臨界資源的信號量M,M的初始值爲1,當獲取到M並發現M的值大於1時,可以反問臨界資源M,且信號量-1,M=0。當進程B也要訪問臨界資源S時,也要首先獲取臨界資源S的信號量M,發現M的值爲0時,無法獲取臨界資源S,進程B阻塞等待。當進程A訪問完臨界資源後,信號量M加1,變爲M=1,同時喚醒進程B,使得B能夠訪問臨界資源S,以此類推,交替訪問臨界資源S。
二 信號量創建:int semget(key_t key, int num_sems, int sem_flags); 成功返回一個相應信號標識符,失敗返回-1
1,key 鍵值,由ftok提供,第一個參數key是整數值(唯一非零),不相關的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序對所有信號量的訪問都是間接的,程序先通過調用semget函數並提供一個鍵,再由系統生成一個相應的信號標識符(semget函數的返回值),只有semget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數返回的信號量標識符。
2,num_sems指定需要的信號數目,它的值幾乎總是1;
3,sem_flags 是一組標誌,當想要當信號量不存在時創建一個新的信號量,可以和值IPC_CREAT做按位或操作。設置了IPC_CREAT標誌後,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。
三 操作 :改變信號量的值
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
1,sem_id 是由semget返回的信號量標識符
2,sembuf
struct sembuf
{
short sem_num;//除非使用一組信號量,否則它爲0
short sem_op;//信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P(等待)操作,
//一個是+1,即V(發送信號)操作。
short sem_flg;//通常爲SEM_UNDO,使操作系統跟蹤信號,
//並在進程沒有釋放該信號量而終止時,操作系統釋放信號量
};
3, nsops:信號操作結構的數量,恆大於或等於1
四 控制 int semctl(int sem_id, int sem_num, int command, ...);
如果有第四個參數,它通常是一個union semum結構,定義如下:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
1,sem_id信號量標識符
2,操作信號在信號集中的編號,第一個信號的編號是0
3,command:
STEVAL:把信號量初始化爲一個已知的值。P這個值通過union semun中的val成員設置,其作用是在信號量第一次使用前對他進行設置
IPC_RMID:刪除一個無需使用的信號量標識符
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
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(Linux specific) */
};
int sem_init(int sem_id)
{
//int semctl(int semid, int semnum, int cmd, ...);
union semun sem;
sem.val = 1;
int ret = semctl (sem_id,0,SETVAL,sem);//信號量的初始化
if(ret == -1)
{
perror("semctl ");
return -1;
}
return 0;
}
int sem_del(int sem_id)
{
int ret = semctl (sem_id,0,IPC_RMID);//刪除信號量
if(ret == -1)
{
perror("semctl ");
return -1;
}
return ret;
}
//信號量的P操作
int sem_p(int sem_id)
{
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = -1;
sem.sem_flg = SEM_UNDO;
int ret = semop(sem_id,&sem,1);
return ret;
}
//信號量的V操作
int sem_v(int sem_id)
{
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = SEM_UNDO;
int ret = semop(sem_id,&sem,1);
return ret;
}