同步和互斥——生产者和消费者进程版

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<semaphore.h> /* 提供了信号量的相关操作 */
#include "error_plp.h" /* 这是我自定义的一个出错处理函数,具体内容见前面的error_plp.h和error_plp.c */
#include<sys/mman.h> /* 提供了共享内存的相关操作 */
#include<fcntl.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include <string.h>

#define BUFFER_SIZE 10 /* 公用环形缓冲区的大小 */
#define PRODUCER_NUM 5 /* 生产者进程的个数 */
#define CONSUMER_NUM 50 /* 消费者进程的个数 */
#define RWRWRW S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH /* 设定创建的文件的访问权限为:用户、组用户和其他用户都可读可写 */


struct ProCon
{
int buff[BUFFER_SIZE];
int nput,nputval,nget,ngetval;
sem_t mutex, nempty,nstored;
}*share;

int nItem = 20;

extern int random_generator(int start, int end); /* 用来生成start~end间(包括start和end,是离散闭区间)的随机数 */
void producer(void);     /* 生产者所执行的代码 */
void consumer(void);     /* 消费者所执行的代码 */
int process_create(pid_t *pid_new, void (*routine)(void));   /* 生成子进程函数。这个函数的接口类似pthread_creat,pid_new用来保存新的子进程的pid;routine是一个函数指针,指向子进程的执行函数 */

int main(void)
{
    int i;
    int fd;
    pid_t pid;
    pid_t pid_producer[PRODUCER_NUM], pid_consumer[CONSUMER_NUM];    /* 这两个数组分别用来保存生产者进程和消费者进程的pid */
    void *ptr;
    int length;
  
 /* 计算共享内存的长度 */
    length = sizeof(struct ProCon);

     /* shm_open是一个POSIX函数,用来打开或创建一个与“/shm”关联的共享内存区 */
    if((fd = shm_open("/shm", O_RDWR | O_CREAT, RWRWRW)) == -1)
    {
        err_exit("shm_open error");   /* 出错提示,可用简单的printf或fprintf代替 */
    }
    if(ftruncate(fd, length) == -1)   /* 截短共享内存的长度到我们所需要的长度 */
    {
        err_exit("ftruncate error");
    }
    if((ptr = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)   /* 将共享内存(内核中的一个区域)映射到进程地址空间 */
    {
        err_exit("mmap error");
    }  
  
     /* 共享内存的变量*/
    share = (struct ProCon *)ptr;
    memset(share,0,sizeof(struct ProCon));

 /* 初始化 */
    sem_init(&share->nstored, 1, 0);     
    sem_init(&share->nempty, 1, BUFFER_SIZE);   
    sem_init(&share->mutex, 1, 1);  
   
    shm_unlink("/shm");  
 
    for(i = 0; i < PRODUCER_NUM; i++)   /* 生成生产者进程 */
    {
        if(process_create(&pid_producer[i], producer) != 0)       /* 生产者进程的执行函数为producer */
        {
             goto end;
            /* kill(0, signum) */    /* 生成失败,尚无好的处理办法 */
        }
    }


    for(i = 0; i < CONSUMER_NUM; i++)   /* 生成消费者进程 */
    {
        if(process_create(&pid_consumer[i], consumer) != 0)       /* 消费者进程的执行函数为consumer */
        {
             goto end;
            /* kill(0, signum) */    /* 生成失败,尚无好的处理办法 */
        }
    }

    for(i = 0; i < PRODUCER_NUM + CONSUMER_NUM; i++)        /* wait处理,避免僵尸进程(zombie) */
    {
        waitpid(0, NULL, 0);
    }
   
end: 
    shm_unlink("/shm");     /* 父进程是最后退出的,所以在他这里删除共享内存区 */

     return 0;
   
}

/* 生产者写10次后退出 */
void producer(void)
{
 int pos,value;
 while(1)
 {
  if (1 == getppid())
   exit(-1);
  sem_wait(&share->nempty);
  sem_wait(&share->mutex);
  if (share->nput >= nItem)
  {
   sem_post(&share->nempty);
   sem_post(&share->nstored);
   sem_post(&share->mutex);
   return;
  }
  pos = share->nput % BUFFER_SIZE;
  value = share->nputval;
  share->buff[pos] = share->nputval;
  share->nput++;
  share->nputval++;
  sem_post(&share->nstored);
  sem_post(&share->mutex);
  
  printf("in pid: %ld, write %5d to position %5d\n", (long)getpid(), value, pos);
  //sleep(random_generator(1, 5)); /* 休眠一段随机的时间(1s~5s,包括端点) */


 }
}

/* 消费者写10次后退出 */
void consumer(void)
{

 int value,pos;
 
 while(1)
 {
  if (1 == getppid())
   exit(-1);
  sem_wait(&share->nstored);
  sem_wait(&share->mutex);
  if (share->nget >=nItem)
  {
   sem_post(&share->nstored);
   sem_post(&share->mutex);
   return;
  }
  pos = share->nget % BUFFER_SIZE;
  value = share->buff[pos];
  share->nget ++;
  share->ngetval ++;
  sem_post(&share->nempty);
  sem_post(&share->mutex);
  
  printf("in pid: %ld, read %5d from position %5d\n", (long)getpid(), value, pos);
  
  
  
 
 }
}


/* 生成子进程函数。这个函数的接口类似pthread_creat,pid_new用来保存新的子进程的pid;routine是一个函数指针,指向子进程的执行函数 */
int process_create(pid_t *pid_new, void (*routine)(void))
{
    pid_t pid;
       
    switch(pid = fork())
    {
        case -1:
            return errno;
            break;
           
        case 0:      /* 子进程执行完routine后退出 */
            routine();
            exit(0);
            break;
           
        default:
            *pid_new = pid;
            return 0;
            break;
    }
}

/********************************random_generator****************************
**since all the random number can be normalized to:                         *
** from start to end, not include start and end.                           *
**   where end>start>0 && start and end are integers                        *
******************************************************************************/
                                                                                                              
int random_generator(int start, int end)
{
    int num;
    struct timeval seed;
    gettimeofday(&seed, NULL); /* 获取当前时间 */
    srand(seed.tv_usec); /* 以当前时间的微秒值作随机种子 */
    num = start + (int)((float)(end-start+1)*(rand()/(float)(RAND_MAX + 1.0))); /* 生成start~end间的随机数 */
    return num;
}

 

 

修改自:http://apps.hi.baidu.com/share/detail/48855270

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值