#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;
}