生产者-消费者问题是操作系统的经典问题,需求是这样的:在系统中创建一个共享内存区域,内存区有长度为1的浮点数组,两个进程分别对共享内存区进行操作,进程A复杂循环向共享内存区域写入数据,B复杂读取由进程A产生的数据。
为了更好的利用信号量,观察效果,A和B进程没有亲缘关系。代码如下
/*进程A,负责发送数据*/
#include <stdlib.h>
#include<unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<sys/sem.h>
#include <sys/msg.h>
#include<time.h>
typedef struct
{
double buff_dat;
} SHAME_BUF;//共享内存数据类型
typedef union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}SEMCTL_UNION;//信号量控制时所需要传递的参数类型
int main(void)
{
int id_sem,id_shm,i=0;//信号量和共享内存标识符
key_t key;//键值
SEMCTL_UNION semctl_arg;// 联合类型变量
struct sembuf semop_buf;//操作信号1的变量
struct sembuf semop_buf2;//操作信号2的变量
SHAME_BUF *shm_buf;//共享内存结构指针
time_t t;
srand((unsigned)time(&t));//随机数
if((key=ftok("/etc/profile",2))<0)//生成键值
{
perror("ftok");
exit(0);
}
if((id_sem=semget(key,2,IPC_CREAT|IPC_EXCL|0666))==-1)//创建信号量集合包含两个信号量
{
if(errno!=EEXIST)
{
perror("semget");
exit(1);
}
if((id_sem=semget(key,2,0))<0)//获取已经存在的信号量集合
{
perror("semget");
exit(2);
}
}
printf("id_sem=%d\n",id_sem);//打印信号量集合的描述符号
semctl_arg.val=0;//初始值为0
/*必须清除*/
memset(&semop_buf,0x00,sizeof(struct sembuf));
memset(&semop_buf2,0x00,sizeof(struct sembuf));
semop_buf.sem_num=0;//第一个信号
semop_buf.sem_op=1;//设置信号量操作 V
semop_buf2.sem_num=1;//信号集的第二个信号
semop_buf2.sem_op=-1;//设置信号量操作 P
if(semctl(id_sem,0,SETVAL,semctl_arg)<0)//设置信号量1初始值0
{
perror("semctl");
exit(3);
}
if(semctl(id_sem,1,SETVAL,semctl_arg)<0)//设置信号量2初始值0
{
perror("semctl");
exit(4);
}
if((id_shm=shmget(key,sizeof(SHAME_BUF),IPC_CREAT|0666))<0)//创建共享内存
{
perror("shmget");
exit(5);
}
if((shm_buf=shmat(id_shm,NULL,0))==(void *)-1)//映射共享内存到本地
{
perror("shmat");
exit(6);
}
printf("id_shm=%d\n",id_shm);//输出共享内存描述符号
shm_buf->buff_dat=0;
while(1)
{
shm_buf->buff_dat=(double)(rand()%100);//产生随机数
sleep((unsigned int)rand()%4);//模仿A产生数据的速度不固定,可能相差0-3秒
i++;
printf ("send %d complete \n",i);//打印完成的信息
if(semop(id_sem,&semop_buf,1)==-1)
{
//信号1要执行V操作,进程会继续执行,写入进程执行,读去进程因为执行P操作被阻塞,等待数据到来
perror("semop");
}
if(semop(id_sem,&semop_buf2,1)==-1)
{
//信号2要执行P操作,被阻塞。等待另一个进程读取完毕
perror("semop");
}
if(i==10)
{
break;
}
}
shmctl(id_shm,IPC_RMID,0);//删除共享内存
semctl(id_sem,IPC_RMID,0);//删除信号量
return 0;
}
</pre><p></p><p><pre name="code" class="cpp">/*进程B,负责打印数据*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<sys/sem.h>
#include <sys/msg.h>
#include<time.h>
typedef struct
{
double buff_dat;
} SHAME_BUF;//共享内存数据类型
typedef union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}SEMCTL_UNION;//信号量控制时所需要传递的参数类型
int main(void)
{
int id_sem,id_shm,i=0;//信号量和共享内存标识符
struct sembuf semop_buf;//进行信号量操作时候需要的变量1
struct sembuf semop_buf2;//进行信号量操作时候需要的变量2
SHAME_BUF *shm_buf;//共享内存结构指针
id_sem=32769;
id_shm=17006600;// ipcs -m查看的
memset(&semop_buf,0x00,sizeof(struct sembuf));
memset(&semop_buf2,0x00,sizeof(struct sembuf));
semop_buf.sem_num=0;//操作第二个信号
semop_buf.sem_op=-1;//P操作
semop_buf2.sem_num=1;//操作第二个信号
semop_buf2.sem_op=1;//V操作
//semop_buf.sem_flg=IPC_NOWAIT;
if((shm_buf=shmat(id_shm,NULL,0))==(void *)-1)//映射共享内存到本地
{
perror("shmat");
exit(0);
}
while(1)
{
/*执行P操作,如果不能执行通。该进程会被阻塞,如果可以执行通,说明发送进程已经写入数据*/
if(semop(id_sem,&semop_buf,1)==-1)
{
perror("semop");
}
printf("the num %d data is %lf\n",i,shm_buf->buff_dat);//读共享内存发过来的数
i++;//计数
if(semop(id_sem,&semop_buf2,1)==-1)//执行V操作,该进程此刻被阻塞,直到发送进程
{
perror("semop");
}
if(i==10)
{
break;
}
}
shmctl(id_shm,IPC_RMID,0);//删除共享内存
semctl(id_sem,IPC_RMID,0);//删除信号量
return 0;
}
进程A负责向共享内存区写入数据,写完后,进程A阻塞,进程B开始打印,进程B打印完成后,A会继续写入,就这样一直重复十次.当初因为忘记了清零
memset(&semop_buf,0x00,sizeof(struct sembuf));
memset(&semop_buf2,0x00,sizeof(struct sembuf));
出现了一些不愉快,参考
http://blog.csdn.net/sahusoft/article/details/9033901,非常感谢。前面几天一直学习linux操作系统的进程间通信,通过消费者生产者问题综合锻炼了下自己的能力,感觉有帮助,代码自己写,但是感觉收获很多。