信号量集数据结构
struct semid_ds { struct ipc_perm sem_perm; //IPC对象数据结构 time_t sem_otime; //最后一次执行semop(PV操作)的时间 time_t sem_ctime; //信号量集状态最后一次改变的时间 unsigned short sem_nsems; //信号量集中信号量的个数 };
信号量集函数
semget函数
功能:
用来创建和访问一个信号量集
原型:
int semget(key_t key, int nsems, int semflg);
参数
key: 信号量集的名字
nsems:信号量集中信号量的个数semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:
成功返回一个非负整数,即该信号量集的标识码;失败返回-1
shmctl函数
功能:
用于控制信号量集
原型:
int semctl(int semid, int semnum, int cmd, ...);
参数
semid:由semget返回的信号量集标识码
semnum:信号量集中信号量的序号(表示对哪个信号量进行操作),从0开始编号
cmd:将要采取的动作,也就是操作的命令(有三个可取值)
cmd 取值如下:
SETVAL | 设置信号量集中的信号量的计数值 |
GETVAL | 获取信号量集中的信号量的计数值 |
IPC_STAT | 把semid_ds结构中的数据设置为信号量集的当前关联值 |
IPC_SET | 在进程有足够权限的前提下,把信号量集的当前关联值设置为semid_ds数据结构中给出的值 |
IPC_RMID | 删除信号量集 |
最后一个参数是 union semun,具体成员根据cmd 的不同而不同
注:下面这个联合并没有出现在任何系统头文件中,需要应用程序声明
union semun { int val; /*used for SETVAL only*/ struct semid_ds *buf; /*used for IPC_SET and IPC_STAT*/ ushort *arry; /*used for GETALL and SETALL*/ }
返回值:
成功返回0;失败返回-1
semop函数
功能:
用来创建和访问一个信号量集
原型:
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数
semid:是该信号量集的标识码,也就是semget函数的返回值sops:是个指向一个结构体的指针(操作的内容在结构体sembuf中)
nsops:信号量的个数
返回值:
成功返回0;失败返回-1
说明:
sembuf结构体
struct sembuf { short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ };
sem_num:是信号量的编号。
sem_op:是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用。当然+-n 和0 都是允许的。需要注意的是只有+n 才确保将马上返回,而-n 和 0 很可能是会阻塞的,+-n 需要进程对信号量集有写的权限,而0 只需要读的权限。
sem_flag:的两个取值是IPC_NOWAIT或SEM_UNDO,设为前者如果当某个信号量的资源为0时进行P操作,此时不会阻塞等待,而是直接返回资源不可用的错误;设为后者,当退出进程时对信号量资源的P操作或V操作撤销;不关心时设置为0(表示操作将会阻塞直到信号量的计数不为0才返回)即可。
当要对一个信号量集中的多个信号量进行操作时,sops 是结构体数组的指针,此时nsops 不为1。此时对多个信号量的操作是作为一个单元原子操作,要么全部执行,要么全部不执行。
综合代码
#include <unistd.h> #include <sys/types.h> #include <sys/msg.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0) 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_create(key_t key) { int semid; semid=semget(key,1,IPC_CREAT | IPC_EXCL | 0666); if(semid==-1) ERR_EXIT("semget"); return semid; } //封装一个方法,只用于打开一个已经创建的信号量级 int sem_open(key_t key) { int semid; //可以都写为0打开已创建的信号量级 semid=semget(key,0,0); if(semid==-1) ERR_EXIT("semget"); return semid; } //对一个信号量进行设置 int sem_setval(int semid,int val) { union semun su; su.val=val; int ret; //对第0个信号量进行操作 //su是设置的参数 ret=semctl(semid,0,SETVAL,su); if(ret==-1) ERR_EXIT("sem_setval"); printf("value updated...\n"); return 0; } //获取信号量级中信号量的计数值 int sem_getval(int semid) { int ret; ret=semctl(semid,0,GETVAL,0); if(ret==-1) ERR_EXIT("sem getval"); printf("current val is %d\n",ret); return ret; } //封装删除信号量级的函数 int sem_d(int semid) { int ret; ret=semctl(semid,0,IPC_RMID,0); if(ret==-1) ERR_EXIT("semctl"); return 0; } //P操作 int sem_p(int semid) { //0表示对信号量级中的第一个信号量进行操作 //-1表示对信号量的操作是P操作 struct sembuf sb={0,-1,0}; int ret; ret=semop(semid,&sb,1); if(ret==-1) ERR_EXIT("semop"); return ret; } //V操作 int sem_v(int semid) { //0表示对信号量级中的第一个信号量进行操作 //1表示对信号量的操作是V操作 struct sembuf sb={0,1,0}; int ret; ret=semop(semid,&sb,1); if(ret==-1) ERR_EXIT("semop"); return ret; } //获取权限 int sem_getmode(int semid) { union semun su; struct semid_ds sem; su.buf=&sem; //将权限获取到一个联合体中 int ret=semctl(semid,0,IPC_STAT,su); if(ret==-1) ERR_EXIT("semctl"); printf("current permissions is %o\n",su.buf->sem_perm.mode); return ret; } //设置权限 int sem_setmode(int semid,char *mode) { union semun su; struct semid_ds sem; su.buf=&sem; int ret=semctl(semid,0,IPC_STAT,su); if(ret==-1) ERR_EXIT("semctl"); printf("current permissions is %o\n",su.buf->sem_perm.mode); sscanf(mode,"%o",(unsigned int*)&su.buf->sem_perm.mode); ret=semctl(semid,0,IPC_SET,su); if(ret==-1) ERR_EXIT("semctl"); printf("permissions updated...\n"); return ret; } void usage(void) { fprintf(stderr,"usage:\n"); //创建一个信号量级 fprintf(stderr,"semtool -c\n"); //删除一个信号量级 fprintf(stderr,"semtool -d\n"); //执行一个P操作 fprintf(stderr,"semtool -p\n"); //执行一个V操作 fprintf(stderr,"semtool -v\n"); //对信号量级中的信号量设置一个初始的计数值 fprintf(stderr,"semtool -s <val>\n"); //获取信号量级中信号量的计数值 fprintf(stderr,"semtool -g\n"); //查看信号量级的权限 fprintf(stderr,"semtool -f\n"); //更改权限 fprintf(stderr,"semtool -m <mode>\n"); } int main(int argc,char *argv[]) { int opt; //用于解析参数 opt=getopt(argc,argv,"cdpvs:gfm:"); if(opt=='?') exit(EXIT_FAILURE); if(opt==-1) { usage(); exit(EXIT_FAILURE); } //利用函数ftok生成关键字 //ftok第一个参数是一个存在的路径,这里用了当前路径 key_t key=ftok(".",'s'); int semid; switch(opt) { //创建的功能 case 'c': sem_create(key); break; //打开一个信号量级,然后进行P操作,然后获取当前信号量的计数值 case 'p': semid=sem_open(key); sem_p(semid); sem_getval(semid); break; //打开一个信号量级,然后进行V操作,然后获取当前信号量的计数值 case 'v': semid=sem_open(key); sem_v(semid); sem_getval(semid); break; //打开信号量级,然后进行删除 case 'd': semid=sem_open(key); sem_d(semid); break; //打开信号量级,然后设置,s后面还有一个参数,可通过optarg来获取 case 's': semid=sem_open(key); sem_setval(semid,atoi(optarg)); 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]); break; } return 0; }