System V 訊號量
訊號量概念:
1.二值訊號量:值為0或者1。若互斥鎖就是這種
2.計數訊號量:值為0~N之間的訊號量
3.計數訊號量集:就是一個或多個訊號量構成一個集合
System V訊號量就是計數訊號量集,而Posix訊號量則是單個計數訊號量
=======================================================
系統中核心維護著每個訊號量集,其維護的資訊結構如下:
struct semid_ds{
struct ipc_perm sem_perm;//操作許可權結構
struct sem* sem_base;//指向訊號量集合的指標
ushort sem_nsems;//訊號量集合中訊號量個數
time_t sem_otime;//上一次semop()操作時間
time_t sem_ctime;//建立時間或者上一次IPC設定的時間
};
struct sem{
ushort_t semval;//訊號量值
shortsempid;//上次成功操作semop的程序ID
ushort_t semncnt;//等待semval大於當前值的執行緒數
ushort_t semzcnt;//等待semval等於0的執行緒數
};
*注意這個結構體是由核心維護的,應用程式編寫用不到這個結構體
======================================================
函式:
1.semget函式:建立一個訊號量集或者訪問一個已經存在的訊號量集
int semget(key_t key,int nsems,int oflag);
返回一個訊號量集的標示符,若出錯返回-1.
引數nsems指定建立集合中訊號量的個數,若僅僅是訪問該訊號量集,則指定為0。在建立完成之後不能對其進行修改。
oflag為IPC_CREAT,建立訊號量集,還可與IPC_EXCL一起用,在一起用時,若該集合已經建立,則返回一個錯誤EEXIST。
IPC_CREAT常與SEM_R(讀)、SEM_A(寫)一起用,指定該訊號集可進行讀寫操作。(SEM_R和SEM_A可能沒有定義,需要自己進行定義,其值為0400、0200)
2.semop函式:對訊號量進行操作
int semop(int semid, struct sembuf* optstr, size_t nops);
optstr為指向下面的結構陣列的指標:
struct sembuf{
short sem_num;//訊號集的第幾個訊號量,從0開始
short sem_op;//訊號量運算元,其數可大於0、等於0,還可以小於0
short sem_flg;//操作標誌
};
* 該結構體僅能保證有上述的成員,它還可能包含其他的成員。
nops指示optstr陣列中有幾個成員。
關於semop的值:
2.1 大於0 其值加到semval上。表示釋放其控制的資源。
(若指定了SEM_UNDO,則從相應訊號量的semadj值中減去sem_op的值)
2.2 等於0 表示呼叫者希望semval為0,若為0,則立即返回,否則阻塞等待。
若指定了IPC_NOWAIT標誌,不為0的時候也會立即返回
2.3 小於0 表示希望等待到semval變為大於或等於sem_op的絕對值。
3.semctl函式:對訊號量進行各種控制操作
int semctl(int sem_id, int semnum, int cmd, /*union semun arg*/);//第四個引數是根據第三引數的情況進行可選的
union semun{//該聯合體需要自己在程式中定義
int val;//訊號量值,僅在SETVAL時使用
struct semid_ds *buf;//僅在IPC_SET和IPC_STAT使用,注意此處的結構體非前面介紹的核心管理的結構體,有一些不一樣
ushort *array;//僅在SETALL和GETALL中使用,訊號量值陣列指標,設定和獲取訊號量集的訊號量值
};
semnum標示訊號集中的某個成員(從0開始)。
cmd值:
GETVAL:返回當前訊號量值semval
SETVAL:設定當前訊號量值semval
GETPID:獲取上次操作的程序PID
GETNCNT:獲取正在等待semval大於當前值的執行緒數
GETZCNT:獲取正在等待semval等於0的執行緒數
GETALL:獲取指定訊號集內每個成員的semval值。通過arg.arrag指標返回
SETALL:設定訊號集每個成員semval的值
IPC_RMID:刪除訊號量集(注意是刪除集合,不是刪除其中的某個訊號量成員)
IPC_SET:設定訊號量集的許可權
IPC_STAT:返回訊號集當前訊號量的狀態
=========================================================
=========================================================
/**********************************
*Date:Fri Apr 11 15:54:50 CST 2014
*System V訊號量的使用
***********************************/
#include <iostream>
extern "C"{
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/time.h>
}
using namespace std;
//需要自己定義此聯合體
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
#define SEM_KEY "SEM_KEY"
#define NUM_SEM 2
#define SEM_R 0400 //使用者讀
#define SEM_A 0200 //使用者寫
//獲取時間
string to_timeStr(time_t otime)
{
struct tm* time = localtime(&otime);
int yy = time->tm_year + 1900;
int mm = time->tm_mon + 1;
int dd = time->tm_mday;
int HH = time->tm_hour;
int MM = time->tm_min;
int ss = time->tm_sec;
char strBuf[25] = {0};
sprintf(strBuf,"%04d/%02d/%02d %02d:%02d:%02d",yy,mm,dd,HH,MM,ss);
return strBuf;
}
int main(int argc,char* argv[])
{
key_t key = ftok(SEM_KEY,0);
if(key == -1){
cerr << "failed to ftok,error:" << strerror(errno) << endl;
return -1;
}
int semfd = semget(key,NUM_SEM,IPC_CREAT|SEM_R|SEM_A);//建立一個訊號集,該訊號集有兩個訊號量,訊號量可進行讀寫操作
if(semfd == -1){
cerr << "failed to create sem,error:" << strerror(errno) << endl;
return -1;
}
else{
cout << "success to create sem" << endl;
}
//對訊號量集進行操作
struct sembuf* buf = NULL;
buf = (struct sembuf*)malloc(sizeof(struct sembuf)*NUM_SEM);//分配對訊號集進行操作的結構體空間
//第一個訊號量操作結構體
buf[0].sem_num = 0;
buf[0].sem_op = 1;
buf[0].sem_flg = IPC_NOWAIT;//不進行等待
//第二個訊號量操作結構體
buf[1].sem_num = 1;
buf[1].sem_op = 2;
buf[1].sem_flg = IPC_NOWAIT;//不進行等待
int res = semop(semfd,buf,2);//陣列buf有兩個元素
if(res == -1){
cerr << "failed tp sem op,error:" << strerror(errno) << endl;
return -1;
}
else{
cout << "success to operator sem" << endl;
}
//對訊號量進行控制
struct semid_ds semInfo;
union semun arg;
arg.buf = &semInfo;
res = semctl(semfd,0,IPC_STAT,arg);//獲取第一個訊號量(下標從0開始)
if(res == -1){
cerr << "failed to get IPC stat,error:" << strerror(errno) << endl;
return -1;
}
else{
cout << "1st->nsems:" << arg.buf->sem_nsems << endl;
cout << "1st->otime:" << to_timeStr(arg.buf->sem_otime) << endl;
}
res = semctl(semfd,1,IPC_STAT,arg);//獲取第二個訊號量
if(res == -1){
cerr << "failed to get IPC stat,error:" << strerror(errno) << endl;
return -1;
}
else{
cout << "2nd->nsems:" << arg.buf->sem_nsems << endl;
cout << "2nd->otime:" << to_timeStr(arg.buf->sem_otime) << endl;
}
//刪除訊號量集
res = semctl(semfd,0,IPC_RMID);//注意IPC_RMID刪除的是訊號量集
if(res == -1){
cerr << "failed to remove sem,error:" << strerror(errno) << endl;
}
else{
cout << "success to remove sem" << endl;
}
return 0;
}