生产者消费者问题——基于共享内存和信号量

生产者消费者问题:
通过共享内存和信号量来处理

这里写图片描述

首先我们必须保证在有空间可以放时,生产者才能放数据,有数据可取时,消费者才能取数据。并且二者对这块空间的访问是互斥的
因此需要定义一个互斥信号量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.若消费者进程先启动,由于无数据可取,会一直阻塞,直到消费者进程放数据才可以进行。
这里写图片描述
这里写图片描述
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值