Linux IPC实践(11) --System V信号量(1)
信號量API
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops);semget
int semget(key_t key, int nsems, int semflg);創(chuàng)建/訪問一個信號量集
參數(shù):
? ?key:?信號集鍵(key)
? ?nsems:信號集中信號量的個數(shù)
? ?semflg:?由九個權(quán)限標(biāo)志構(gòu)成,它們的用法和創(chuàng)建文件時使用的mode模式標(biāo)志一致
返回值:成功返回一個非負(fù)整數(shù),即該信號集的標(biāo)識碼;失敗返回-1;
? ?此時創(chuàng)建的信號量集中的每一個信號量都會有一個默認(rèn)值:?0,?如果需要更改該值,?則需要調(diào)用semctl函數(shù)->更改初始值;
/** 示例1: 封裝一個創(chuàng)建一個信號量集函數(shù) 該信號量集包含1個信號量; 權(quán)限為0666 **/ int sem_create(key_t key) {int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);if (semid == -1)err_exit("sem_create error");return semid; } /** 示例2: 打開一個信號量集 nsems(信號量數(shù)量)可以填0, semflg(信號量權(quán)限)也可以填0, 表示使用默認(rèn)的權(quán)限打開 **/ int sem_open(key_t key) {int semid = semget(key, 0, 0);if (semid == -1)err_exit("sem_open error");return semid; }shmctl
int semctl(int semid, int semnum, int cmd, ...);控制信號量集
參數(shù)
? ?semid:由semget返回的信號集標(biāo)識碼
? ?semnum:信號集中信號量的序號(注意:?從0開始The?semaphores?in?a?set?are?numbered?starting?at?0.)
? ?cmd:將要采取的動作(常用取值如下)
? ?如果該函數(shù)需要第四個參數(shù)(有時是不需要第四個參數(shù)的,?取決于cmd的取值),?則程序中必須定義如下的聯(lián)合體:
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)*/ }; //struct semid_ds : Linux內(nèi)核為System V信號量維護(hù)的數(shù)據(jù)結(jié)構(gòu) struct semid_ds {struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Last change time */unsigned long sem_nsems; /* No. of semaphores in set */ }; /** 示例1: 將信號量集semid中的第一個信號量的值設(shè)置成為value(SETVAL) 注意: semun聯(lián)合體需要自己給出(從man-page中拷貝出來即可) **/ 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_setval(int semid, int value) {union semun su;su.val = value;if (semctl(semid, 0, SETVAL, su) == -1)err_exit("sem_setval error");return 0; } /** 示例2: 獲取信號量集中第一個信號所關(guān)聯(lián)的值(GETVAL) 注意: 此時第四個參數(shù)可以不填, 而信號量所關(guān)聯(lián)的值可以通過semctl的返回值返回(the value of semval.) **/ int sem_getval(int semid) {int value = semctl(semid, 0, GETVAL);if (value == -1)err_exit("sem_getval error");return value;return 0; } /** 示例3: 刪除一個信號量集(注意是刪除整個集合)IPC_RMID Immediately remove(立刻刪除) the semaphore set, awakening all processes blocked in semop(2) calls on the set (with an error return and errno set to EIDRM)[然后喚醒所有阻塞在該信號量上的進(jìn)程]. The argument semnum is ignored[忽略第二個參數(shù)]. **/ int sem_delete(int semid) {if (semctl(semid, 0, IPC_RMID) == -1)err_exit("sem_delete error");return 0; }//測試代碼 int main(int argc,char *argv[]) {int semid = sem_create(0x1234); //創(chuàng)建一個信號量集sem_setval(semid, 500); //設(shè)置值cout << sem_getval(semid) << endl; //獲取值sleep(10);sem_delete(semid); //刪除該集合 } /**示例4: 獲取/設(shè)置信號量的權(quán)限 注意:一定要設(shè)定struct semid_ds結(jié)構(gòu)體, 以指定使用semun的哪個字段 **/ int sem_getmode(int semid) {union semun su;// 注意: 下面這兩行語句一定要設(shè)定.// (告訴內(nèi)核使用的semun的哪個字段)struct semid_ds sd;su.buf = &sd;//if (semctl(semid, 0, IPC_STAT, su) == -1)err_exit("sem_getmode error");printf("current permissions is: %o\n", su.buf->sem_perm.mode);return 0; } int sem_setmode(int semid, char *mode) {union semun su;// 注意: 下面這兩行語句一定要設(shè)定.// (告訴內(nèi)核使用的semun的哪個字段)struct semid_ds sd;su.buf = &sd;//sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);if (semctl(semid, 0, IPC_SET, su) == -1)err_exit("sem_setmode error");return 0; }semop
int semop(int semid, struct sembuf *sops, unsigned nsops);? ?用來操縱一個信號量集,?以實現(xiàn)P,V操作
參數(shù):
? ?semid:是該信號量的標(biāo)識碼,也就是semget函數(shù)的返回值
? ?sops:是個指向一個結(jié)構(gòu)數(shù)組(如果信號量集中只有一個信號量的話,?只有一個結(jié)構(gòu)體也可)的指針
? ?nsops:所設(shè)置的信號量個數(shù)(如果nsops>1話,?需要將sops[第二個參數(shù)]設(shè)置成為一個結(jié)構(gòu)數(shù)組,?具體參考Man-Page給出的示例代碼),?第三個參數(shù)其實也指出第二個參數(shù)所表示對象的個數(shù);
//sembuf結(jié)構(gòu)體 struct sembuf {unsigned short sem_num; /*semaphore number:信號量的編號(從0開始)*/short sem_op; /* semaphore operation(+1, 0, -1) */short sem_flg; /* operation flags: 常用取值為SEM_UNDO(解釋見下) */ };? ?sem_op是信號量一次PV操作時加減的數(shù)值,一般只會用到兩個值,一個是“-1”,也就是P操作,等待信號量變得可用;另一個是“+1”,也就是V操作,發(fā)出信號量已經(jīng)變得可用,?該參數(shù)還可以等于0,?表示進(jìn)程將阻塞直到信號量的值等于0;
? ?sem_flg有三個取值:?SEM_UNDO(在進(jìn)程結(jié)束時,?將該進(jìn)程對信號量的操作復(fù)原[即:取消該進(jìn)程對信號量所有的操作],?推薦使用),?IPC_NOWAIT(非阻塞)或0(默認(rèn)操作,?并不撤銷操作);
/** 示例: P,V操作封裝 **可以將sembuf的第三個參數(shù)設(shè)置為IPC_NOWAIT/0, 以查看程序的狀態(tài)的變化 **/ int sem_P(int semid) {struct sembuf sops = {0, -1, SEM_UNDO};if (semop(semid, &sops, 1) == -1)err_exit("sem_P error");return 0; } int sem_V(int semid) {struct sembuf sops = {0, +1, SEM_UNDO};if (semop(semid, &sops, 1) == -1)err_exit("sem_V error");return 0; }/** 信號量綜合運用示例: 編譯完成之后, 直接運行./semtool, 程序?qū)⒋蛴≡摴ぞ叩挠梅? 下面的這些函數(shù)調(diào)用, 只不過是對上面所封裝函數(shù)的稍稍改動, 理解起來并不困難; **/ //semtool.cpp #include "Usage.h"int main(int argc,char *argv[]) {int opt = getopt(argc, argv, "cdpvs:gfm:");if (opt == '?')exit(EXIT_FAILURE);else if (opt == -1){usage();exit(EXIT_FAILURE);}key_t key = ftok(".", 's');int semid;switch (opt){case 'c':sem_create(key);break;case 'd':semid = sem_open(key);sem_delete(semid);break;case 'p':semid = sem_open(key);sem_P(semid);sem_getval(semid);break;case 'v':semid = sem_open(key);sem_V(semid);sem_getval(semid);break;case 's':semid = sem_open(key);sem_setval(semid, atoi(optarg));sem_getval(semid);break;case 'g':semid = sem_open(key);sem_getval(semid);break;case 'f':semid = sem_open(key);sem_getmode(semid);break;case 'm':semid = sem_open(key);sem_setmode(semid, argv[2]);sem_getmode(semid);break;default:break;}return 0; } //Usage.h #ifndef USAGE_H_INCLUDED #define USAGE_H_INCLUDED#include <iostream> #include <string>#include <stdio.h> #include <stdlib.h> #include <string.h> #include <string.h>#include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/msg.h> #include <sys/shm.h> #include <sys/mman.h> #include <sys/sem.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <grp.h> #include <pwd.h> #include <time.h> #include <errno.h> #include <mqueue.h> using namespace std; inline void err_quit(std::string message); inline void err_exit(std::string message);void usage() {cerr << "Usage:" << endl;cerr << "./semtool -c #create" << endl;cerr << "./semtool -d #delte" << endl;cerr << "./semtool -p #signal" << endl;cerr << "./semtool -v #wait" << endl;cerr << "./semtool -s <val> #set-value" << endl;cerr << "./semtool -g #get-value" << endl;cerr << "./semtool -f #print-mode" << endl;cerr << "./semtool -m <mode> #set-mode" << endl; }int sem_create(key_t key) {int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);if (semid == -1)err_exit("sem_create error");return semid; } int sem_open(key_t key) {int semid = semget(key, 0, 0);if (semid == -1)err_exit("sem_open error");return semid; }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_getmode(int semid) {union semun su;// 注意: 下面這兩行語句一定要設(shè)定.// (告訴內(nèi)核使用的semun的哪個字段)struct semid_ds sd;su.buf = &sd;//if (semctl(semid, 0, IPC_STAT, su) == -1)err_exit("sem_getmode error");printf("current permissions is: %o\n", su.buf->sem_perm.mode);return 0; } int sem_setmode(int semid, char *mode) {union semun su;// 注意: 下面這兩行語句一定要設(shè)定.// (告訴內(nèi)核使用的semun的哪個字段)struct semid_ds sd;su.buf = &sd;//sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);if (semctl(semid, 0, IPC_SET, su) == -1)err_exit("sem_setmode error");return 0; } int sem_getval(int semid) {int value = semctl(semid, 0, GETVAL);if (value == -1)err_exit("sem_getval error");cout << "current value: " << value << endl;return value; } int sem_setval(int semid, int value) {union semun su;su.val = value;if (semctl(semid, 0, SETVAL, su) == -1)err_exit("sem_setval error");return 0; }int sem_delete(int semid) {if (semctl(semid, 0, IPC_RMID) == -1)err_exit("sem_delete error");return 0; }// 為了能夠打印信號量的持續(xù)變化, 因此sem_flg我們并沒用SEM_UNDO // 但是我們推薦使用SEM_UNDO int sem_P(int semid) {struct sembuf sops = {0, -1, 0};if (semop(semid, &sops, 1) == -1)err_exit("sem_P error");return 0; } int sem_V(int semid) {struct sembuf sops = {0, +1, 0};if (semop(semid, &sops, 1) == -1)err_exit("sem_V error");return 0; }inline void err_quit(std::string message) {std::cerr << message << std::endl;exit(EXIT_FAILURE); } inline void err_exit(std::string message) {perror(message.c_str());exit(EXIT_FAILURE); }#endif // USAGE_H_INCLUDED
附-Makefile
.PHONY: clean all CC = g++ CPPFLAGS = -Wall -g BIN = semtool SOURCES = $(BIN.=.cpp) all: $(BIN)%.o: %.c $(CC) $(CPPFLAGS) -c $^ -o $@ main: main.o$(CC) $(CPPFLAGS) $^ -lrt -o $@clean:-rm -rf $(BIN) *.o bin/ obj/ core總結(jié)
以上是生活随笔為你收集整理的Linux IPC实践(11) --System V信号量(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是SID
- 下一篇: 数据结构拾遗(2) --红黑树的设计与实