System信号量集主要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);
创建/访问一个信号量集
参数:
key: 信号集键(key)
nsems:信号集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志一致
返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1;
此时创建的信号量集中的每一个信号量都会有一个默认值: 0, 如果需要更改该值, 则需要调用semctl函数->更改初始值;
我们将创建和打开分开封装,对于不关心的参数直接添0:
-
-
-
-
- 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;
- }
shmctl
- int semctl(int semid, int semnum, int cmd, ...);
控制信号量集
参数
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号(注意: 从0开始The semaphores in a set are numbered starting at 0.)
cmd:将要采取的动作(常用取值如下)
如果该函数需要第四个参数(有时是不需要第四个参数的, 取决于cmd的取值), 则程序中必须定义如下的联合体:
- union semun
- {
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- struct seminfo *__buf;
- };
-
- struct semid_ds
- {
- struct ipc_perm sem_perm;
- time_t sem_otime;
- time_t sem_ctime;
- unsigned long sem_nsems;
- };
-
-
-
- union semun
- {
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- struct seminfo *__buf;
-
- };
- 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_getval(int semid)
- {
- int value = semctl(semid, 0, GETVAL);
- if (value == -1)
- err_exit("sem_getval error");
- return value;
- return 0;
- }
-
-
-
- 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);
- sem_setval(semid, 500);
- cout << sem_getval(semid) << endl;
- sleep(10);
- sem_delete(semid);
- }
-
-
-
- int sem_getmode(int semid)
- {
- union semun su;
-
-
-
- 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;
-
-
- 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);
用来操纵一个信号量集, 以实现P,V操作
参数:
semid:是该信号量的标识码,也就是semget函数的返回值
sops:是个指向一个结构数组(如果信号量集中只有一个信号量的话, 只有一个结构体也可)的指针
nsops:所设置的信号量个数(如果nsops>1话, 需要将sops[第二个参数]设置成为一个结构数组, 具体参考Man-Page给出的示例代码), 第三个参数其实也指出第二个参数所表示对象的个数;
-
- struct sembuf
- {
- unsigned short sem_num;
- short sem_op;
- short sem_flg;
- };
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是V操作,发出信号量已经变得可用, 该参数还可以等于0, 表示进程将阻塞直到信号量的值等于0;
sem_flg有三个取值: SEM_UNDO(在进程结束时, 将该进程对信号量的操作复原[即:取消该进程对信号量所有的操作], 推荐使用), IPC_NOWAIT(非阻塞)或0(默认操作, 并不撤销操作);
-
-
-
- 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;
- }
下面我们封装一个信号量操作函数工具,将主要的操作封装起来,可以像命令一样使用。
-
-
-
-
-
- #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;
- }
-
-
- #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;
- struct semid_ds *buf;
- unsigned short *array;
- struct seminfo *__buf;
- };
-
- int sem_getmode(int semid)
- {
- union semun su;
-
-
-
- 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;
-
-
- 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;
- }
-
-
-
- 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
附:ftok函数
系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
返回值:
当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。
在我们获取到key之后,就可以使用该key作为某种方法的进程间通信的key值。