信号量集数据结构
struct semid_ds {
struct ipc_perm sem_perm; //IPC对象数据结构
time_t sem_otime; //最后一次执行semop(PV操作)的时间
time_t sem_ctime; //信号量集状态最后一次改变的时间
unsigned short sem_nsems; //信号量集中信号量的个数
};
信号量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: 信号量集的名字
nsems:信号量集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:
成功返回一个非负整数,即该信号量集的标识码;失败返回-1
semctl函数
功能:用于控制信号量集
int semctl(int semid, int semnum, int cmd, ...);
参数
semid:由semget返回的信号量集标识码
semnum:信号量集中信号量的序号(表示对哪个信号量进行操作),从0开始编号
cmd:将要采取的动作,也就是操作的命令(有三个可取值)
返回值:
成功返回0;失败返回-1
最后一个参数是 union semun,具体成员根据cmd 的不同而不同,cmd 取值如下:
SETVAL | 设置信号量集中的信号量的计数值 |
GETVAL | 获取信号量集中的信号量的计数值 |
IPC_STAT | 把semid_ds结构中的数据设置为信号量集的当前关联值 |
IPC_SET | 在进程有足够权限的前提下,把信号量集的当前关联值设置为semid_ds数据结构中给出的值 |
IPC_RMID | 删除信号量集 |
注:下面这个联合并没有出现在任何系统头文件中,需要应用程序声明
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)*/
};
semop函数
功能:实现P、V操作
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;
}