进程间通讯之---信号量浅谈以及小例子

           众所周知,进程间通讯有三种方式,信号量、消息队列和共享内存。不过信号量我的感受不像通讯,其实就是一个锁的东西。html

                 这部份内容分几个部分数据结构

                1.API函数

                 建立信号量   int semget(key_t key,int nsems,int semflg);  返回值就是信号量标识semid  spa

key:所建立或打开信号量集的键值。
nsems:建立的信号量集中的信号量的个数,该参数只在建立信号量集时有效。
flag:调用函数的操做类型,也可用于设置信号量集的访问权限,二者经过or表示
    操做信号量   int semctl(int semid,int semnum,int cmd, /*union semun arg*/);  cmd表示操做类型,具体本身查资料吧,内容太大了;
   PV 操做经过调用semop函数来实现:int semop(int semid,struct sembuf *sops,size_t nsops);
             这里涉及到两个内核的数据结构,一个是union semun ,另外一个是sembuf
    先说sembuf,是一个结构体,要熟记,结构以下:
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flg;
} 在sembuf结构中,sem_num是相对应的 信号量集 中的某一个资源,因此其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。sem_op指明所要执行的操做,sem_flg说明 函数 semop的行为。sem_op的值是一个整数,如表2所示,列出了详细sem_op的值及所对应的操做。
sem_op值详解(关键)
Sem_op
操 做
正数
释放相应的资源数,将sem_op的值加到信号量的值上
0
进程阻塞直到信号量的相应值为0,当信号量已经为0,函数当即返回。若是信号量的值不为0,则依据sem_flg的IPC_NOWAIT位决定函数动做。sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN。sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,而后进程挂起直到下述状况发生。信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回;此信号量被删除(只有超级用户或建立用户进程拥有此权限),函数smeop出错返回EIDRM;进程捕捉到信号,并从信号处理函数返回,在此状况将此信号量的semncnt值减1,函数semop出错返回EINTR
负数
请求sem_op的绝对值的资源。若是相应的资源数能够知足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。当相应的资源数不能知足请求时,这个操做与sem_flg有关。sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN。sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,而后进程挂起直到下述状况发生:当相应的资源数能够知足请求,该信号的值减去sem_op的绝对值。成功返回;此信号量被删除(只有超级用户或建立用户进程拥有此权限),函数smeop出错返回EIDRM:进程捕捉到信号,并从信号处理函数返回,在此状况将此信号量的semncnt值减1,函数semop出错返回EINTR
而后再说semun:
union semun
{
     int val;
     struct semid_ds *buf;
     unsigned short int *array;
     struct seminfo *__buf;
};      这里面要用到的变量是val

2 直接上小例子吧,这样容易理解:producer.c 创建一个信号量 每隔3秒对val值不停减1;customer.c 读取val值,直到val=0
producer.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
union semun//因为类型是union 须要在这里初始化一下
{
     int val;
     struct semid_ds *buf;
     unsigned short int *array;
     struct seminfo *__buf;
};
int main(int argc, char const *argv[])
{
	int semid;
	key_t key;
	key=ftok(".",3);  //ftok的做用就是计算一个key值供使用,通常用当前目录文件结点索引号来计算,如指定文件的索引节点号为65538,换算成16进制为0x10002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x2610002。
	semid=semget(key,1,IPC_CREAT|0666);//建立并获得信号量id
	if(semid==-1){
		perror("semget");
		exit(1);
	}
	printf("my semid is %d \n",semid);
	struct sembuf sbuf={0,-1,IPC_NOWAIT};// 上面sembuf结构体介绍中说,当sem_op=负数,请求sem_op的绝对值的资源。若是相应的资源数能够知足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。当相应的资源数不能知足请求时,这个操做与sem_flg有关。sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN。
	union semun semopts; 
	semopts.val=5;  //对val赋值
	if((semctl(semid,0,SETVAL,semopts))==-1){  // SETVAL就是给semun.val赋值的动做
		perror("semctl");
		exit(1);
	}
	printf("%s\n","ke" );
	while(1){
		if(semop(semid,&sbuf,1)==-1)  // 这个操做就是semop来对{0,-1,IPC_NOWAIT}对应的动做  不断-1,直到=0
			exit(1);
		sleep(3);
	}
	return 0;
}
customer.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
int main(int argc, char const *argv[])
{
	int semid,semval;
	key_t key;
	key=ftok(".",3);  //必须仍然用这两个值计算,否则信号量对应不起来,固然前提是两个代码位于相同的路径下
	semid=semget(key,1,IPC_CREAT|0666); 
	if(semid==-1){
		perror("semget");
		exit(1);
	}
	int val;
	while(1){
		if((semval=semctl(semid,0,GETVAL,0))==-1)//GETVAL获得semun.val值
			exit(1);
		if(semval>0)
			printf("val is %d\n",semval);
		else{
			printf("stop!\n");
			break;
		}
		sleep(3);
	}
	return 0;
}
结果:
val is 4
val is 3
val is 2
val is 1
stop!
 两个进程确实通讯成功。
相关文章
相关标签/搜索