生产者消费者问题:
通过共享内存和信号量来处理
首先我们必须保证在有空间可以放时,生产者才能放数据,有数据可取时,消费者才能取数据。并且二者对这块空间的访问是互斥的
因此需要定义一个互斥信号量sem_mutex。再者,定义信号量sem_full表示可放块数和sem_empty表示可取块数来进行同步。
这就需要将读索引和写索引保存在创建的共享内存头部,另外将数据块大小和数据块个数也一块保存在头部。
其次,定义一个结构体来保存关于共享内存和信号量的操作等各种信息。
先定义一个shmfifo.h头文件定义这两个结构体和函数声明:
#ifndef __SHMFIFO_H__
#define __SHMFIFO_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
//共享内存的头部信息
typedef struct shmhead
{
int rd_idx;//读索引
int wr_idx;//写索引
int blocks;//数据块的个数
int blksz;//块大小
}head_t;
//整个共享内存的信息
typedef struct shmfifo
{
head_t* head;//指向共享内存的其实位置(头部位置)
char* p_load;//指向真正存数据的地方
int shmid;//共享内存的标识符
int sem_full;//表示还有几个可以存放
int sem_empty;//表示还有几个可以取
int sem_mutex;//互斥量,互斥访问共享内存
}shmfifo_t;
shmfifo_t* shmfifo_init(int key,int blocks,int blksz);
void shmfifo_put(shmfifo_t* fifo,const void* buf);
void shmfifo_get(shmfifo_t* fifo,void* buf);
void shmfifo_destroy(shmfifo_t *fifo);
#endif//__SHMFIFO_H__
shmfifo.c文件写函数定义:
#include "shmfifo.h"
union semun
{
int val;
};
//p操作
void p(int semid)
{
struct sembuf sb[1];
sb[0].sem_num=0;
sb[0].sem_op=-1;
sb[0].sem_flg=0;
semop(semid,sb,1);
}
//v操作
void v(int semid)
{
struct sembuf sb[1];
sb[0].sem_num=0;
sb[0].sem_op=1;
sb[0].sem_flg=0;
semop(semid,sb,1);
}
//初始化
shmfifo_t* shmfifo_init(int key,int blocks,int blksz)
{
//申请一块内存来保存shmfifo_t
shmfifo_t* p=(shmfifo_t*)malloc(sizeof(shmfifo_t));
if(p==NULL)
return NULL;
int len=sizeof(head_t)+blocks*blksz;
int shmid=shmget(key,len,0);//先打开
if(shmid==-1)//不存在,创建
{
shmid=shmget(key,len,IPC_CREAT|0644);
if(shmid==-1)//创建失败
{
perror("shmget");
exit(1);
}
p->head=(head_t*)shmat(shmid,NULL,0);
p->head->rd_idx=0;
p->head->wr_idx=0;
p->head->blocks=blocks;
p->head->blksz=blksz;
p->p_load=(char*)(p->head+1);
p->shmid=shmid;
p->sem_full=semget(key,1,IPC_CREAT|0644);
p->sem_empty=semget(key+1,1,IPC_CREAT|0644);
p->sem_mutex=semget(key+2,1,IPC_CREAT|0644);
union semun su;
su.val=blocks;
semctl(p->sem_full,0,SETVAL,su);
su.val=0;
semctl(p->sem_empty,0,SETVAL,su);
su.val=1;
semctl(p->sem_mutex,0,SETVAL,su);
}
else//存在,直接打开
{
p->head=(head_t*)shmat(shmid,NULL,0);
p->p_load=(char*)(p->head+1);
p->shmid=shmid;
p->sem_full=semget(key,0,0);
p->sem_empty=semget(key+1,0,0);
p->sem_mutex=semget(key+2,0,0);
}
return p;
}
//向共享内存中放数据
void shmfifo_put(shmfifo_t* fifo,const void* buf)
{
p(fifo->sem_full);//先申请可以放数据的地方
p(fifo->sem_mutex);//再申请锁
memcpy(fifo->p_load+fifo->head->wr_idx*fifo->head->blksz,buf,fifo->head->blksz);//拷贝数据
fifo->head->wr_idx=(fifo->head->wr_idx+1)%fifo->head->blocks;//更新写索引
v(fifo->sem_mutex);
v(fifo->sem_empty);
}
//从共享内存中取数据
void shmfifo_get(shmfifo_t* fifo,void* buf)
{
p(fifo->sem_empty);//先申请可以取得数据
p(fifo->sem_mutex);//再申请锁
memcpy(buf,fifo->p_load+fifo->head->rd_idx*fifo->head->blksz,fifo->head->blksz);//拷贝数据
fifo->head->rd_idx=(fifo->head->rd_idx+1)%fifo->head->blocks;//更新读索引
v(fifo->sem_mutex);
v(fifo->sem_full);
}
//资源释放
void shmfifo_destroy(shmfifo_t *fifo)
{
shmdt(fifo->head);
shmctl(fifo->shmid,IPC_RMID,0);
semctl(fifo->sem_full,0,IPC_RMID,0);
semctl(fifo->sem_empty,0,IPC_RMID,0);
semctl(fifo->sem_mutex,0,IPC_RMID,0);
}
producer.c生产者放数据:
#include "shmfifo.h"
typedef struct person
{
char name[10];
int age;
}person_t;
int main()
{
shmfifo_t *fifo=shmfifo_init(12,3,sizeof(person_t));
person_t *person=(person_t*)malloc(sizeof(person_t));
int i=0;
for(i=0;i<10;i++)
{
sprintf(person->name,"00%d",i);
person->age=i+20;
shmfifo_put(fifo,person);
printf("put %d ok\n",i);
}
return 0;
}
consumer.c消费者取数据:
#include "shmfifo.h"
typedef struct person
{
char name[10];
int age;
}person_t;
int main()
{
shmfifo_t *fifo=shmfifo_init(12,3,sizeof(person_t));
person_t *person=(person_t*)malloc(sizeof(person_t));
int i=0;
for(i=0;i<10;i++)
{
shmfifo_get(fifo,person);
printf("name:%s age:%d\n",person->name,person->age);
sleep(1);
}
shmfifo_destroy(fifo);
return 0;
}
执行结果:
1.若生产者进程先启动,将三个数据块空间放满之后就阻塞在那里 ,直到消费者进程取走数据才可以继续放。
2.若消费者进程先启动,由于无数据可取,会一直阻塞,直到消费者进程放数据才可以进行。