进程间通信之

时间:2021-06-21 19:01:50

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

                 这部分内容分几个部分

                1.API

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

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!
 两个进程确实通信成功。