消费者生产者实验

生产者与消费者问题

实验目的

了解和熟悉linux系统下的信号量集和共享内存。

实验任务

使用linux系统提供的信号量集和共享内存实现生产者和消费者问题。

实验要求:

  1. 写两个程序,一个模拟生产者过程,一个模拟消费者过程;
  2. 创建一个共享内存模拟生产者-消费者问题中缓冲队列,该缓冲队列有N(例如N=10)个缓冲区,每个缓冲区的大小为1024B,每个生产者和消费者对缓冲区必须互斥访问;
  3. 由第一个生产者创建信号量集和共享内存,其他生产者和消费者可以使用该信号量集和共享内存;
  4. 生产者程序:生产者生产产品(即是从键盘输入长度小于1024B的字符)放入空的缓冲区;
  5. 消费者程序:消费者消费产品(即从满的缓冲区中取出内容在屏幕上打印出来),然后满的缓冲区变为空的缓冲区;
  6. 多次运行生产者程序和消费者进程,可以产生多个生产者进程和多个消费者进程,这些进程可以共享这些信号量和共享内存,实现生产者和消费者问题;
  7. 在生产者程序程序中,可以选择:
    1. 生产产品;
    2. 退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用;
    3. 删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。
  8. 在消费者程序中,可以选择:
  1. 消费产品;
  2. 退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用;
  3. 删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。

实验代码:

生产者代码:

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/types.h>

#include<sys/sem.h>

#include<sys/shm.h>

#define N 4

#define size  N*1024+8

union semun{

    int val;

    struct semid_ds *buf;

    unsigned short *array;

};    //信号量集 

int main(){

    //记录缓冲区开始的位置和结束的位置

    int* start;

    int* end;

       /*****申请共享内存空间*****/ 

    int shmid;

    char *viraddr;

    char buffer[1024];

    shmid=shmget(8888,size,0666|IPC_CREAT);

    viraddr=(char*)shmat(shmid,0,0);

    start = (int*)viraddr;//缓冲区开始有的位置 

    end = (int*)(viraddr+4);//缓冲区开始结束的位置 

    /*****定义PV操作*****/

    struct sembuf P,V;

    P.sem_num=0;

    P.sem_op=-1;

    P.sem_flg=SEM_UNDO;

    V.sem_num=0;

    V.sem_op=1;

    V.sem_flg=SEM_UNDO;

    /*****申请只有一个信号量的信号量集*****/

    int emptyid;

    int fullid;                                 //同步信号量

    int mutex;  //互斥信号量

 union semun arg;                                 

    if(semget(1000,1,IPC_CREAT|IPC_EXCL)!= -1){

    

        emptyid=semget(1000,1,IPC_CREAT|0666);

     fullid=semget(2000,1,IPC_CREAT|0666);

      mutex=semget(3000,1,IPC_CREAT|0666);\

            /*****分别对每个信号量赋初值*****/

       

        arg.val=N;

        semctl(emptyid,0,SETVAL,arg);

        arg.val=0;

        semctl(fullid,0,SETVAL,arg);

        arg.val=1;

        semctl(mutex,0,SETVAL,arg);

        *end = *start=0;

        puts("第一个生产者初始化\n");

    }else{

        printf("这不是第一个生产者\n");

        emptyid=semget(1000,1,0666);

        fullid=semget(2000,1,0666);

        mutex=semget(3000,1,0666);

    }

    

    int flag =0;

while(1){

    puts("1 生产产品,退出,撤销信号量集、释放共享内存");

    scanf("%d",&flag);

    getchar();

    switch(flag){

        case 1:

            start = (int*)viraddr;//缓冲区开始有的位置 

            end = (int*)(viraddr+4);//缓冲区开始结束的位置 

            char* p = viraddr+8+(*end)*1024;//共享内存正文开始位置

            semop(emptyid,&P,1);

            semop(mutex,&P,1);

            puts("输入生产的信息:");

            fgets(buffer,1024,stdin);

            strcpy(p,buffer);

            *end = (*end+1)%N;

            semop(mutex,&V,1);

            semop(fullid,&V,1);

            printf("生产成功 start=%d  end=%d\n",*start,*end);

            break;

        case 2:

            printf("退出成功\n");

            exit(0);

            break;

        case 3:

            shmdt(viraddr);

            /*****撤销信号量集、释放共享内存*****/

            shmctl(shmid,IPC_RMID,0);

            semctl(emptyid,IPC_RMID,0);

            semctl(fullid,IPC_RMID,0);

            semctl(mutex,IPC_RMID,0);

            printf("撤销成功,退出\n");

            exit(0);

            break;      

    }       

}

    return 0;

消费者代码:

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/types.h>

#include<sys/sem.h>

#include<sys/shm.h>

#define N 4

#define size  N*1024+8

int main(){

    

    //记录缓冲区开始的位置和结束的位置

    intstart;

    intend

        /*****申请只有一个信号量的信号量集*****/

    int emptyid;

    int fullid;                                 //同步信号量

    int mutex;                                  //互斥信号量 

    emptyid=semget(1000,1,0666);

    fullid=semget(2000,1,0666);

    mutex=semget(3000,1,0666);

    /*****定义PV操作*****/

    struct sembuf P,V;

    P.sem_num=0;

    P.sem_op=-1;

    P.sem_flg=SEM_UNDO;

    V.sem_num=0;

    V.sem_op=1;

    V.sem_flg=SEM_UNDO;

    /*****申请共享内存空间*****/ 

    int shmid;

    char *viraddr;

    char buffer[1024];

    shmid=shmget(8888,size,0666|IPC_CREAT);

    viraddr=(char*)shmat(shmid,0,0);

    

    int flag =0;

while(1){

    puts("1 消费产品,退出,撤销信号量集、释放共享内存");

    scanf("%d",&flag);

    getchar();

    switch(flag){

        case 1:

            start = (int*)viraddr;//缓冲区开始有的位置 

            end = (int*)(viraddr+4);//缓冲区开始结束的位置 

            charp = viraddr+8+(*start)*1024;

            //    消费代码段 

                semop(fullid,&P,1);

            semop(mutex,&P,1);

            printf("消费的消息是%s",p);

            *start = (*start+1)%N;

            printf(" start=%d ",*start);

            printf(" end=%d \n",*end);

            semop(mutex,&V,1);

            semop(emptyid,&V,1);

            break;

        case 2:

            printf("退出成功\n");

            exit(0);

            break;

        case 3:

        shmdt(viraddr);

            /*****撤销信号量集、释放共享内存*****/

            shmctl(shmid,IPC_RMID,0);

            semctl(emptyid,IPC_RMID,0);

            semctl(fullid,IPC_RMID,0);

            semctl(mutex,IPC_RMID,0);

            printf("撤销成功,退出\n");

            exit(0);

            break;      

    }       

}

}

运行结果:

第一个生产者:

第二个生产者:

消费者:

实验总结:

实验分析:实验的核心是信号量和共享内存的使用,功能选择是比较简单的switch case 语句。

信号量:实验一共使用了三个信号量,其中emptyid和fullid,用于进程同步,mutex用于进程互斥,使用时先P(emptyid/fullid)再P(mutex)可以预防死锁。

判断是不是第一个生产者通过semget(1000,1,IPC_CREAT|IPC_EXCL)!= -1来判断,满足条件则对共享变量和信号量进行创建。

start

end

p

共享内存:共享内存按这个格式设置:

其中start用于指明缓冲区开始有的位置,end用于指明缓冲区结束的位置,p是共享内存开始的位置;所以共享内存的大小是4*2+1024*N,N个缓冲区。

实验中所有的key(用来指明共享内存或者信号量的)为了不同进程使用的是同一个,全部设置为常数。

遇到的问题:

1. 开始没想到怎么判断是不是第一个生产者,解决办法是通过semget(1000,1,IPC_CREAT|IPC_EXCL)!= -1来设置。

2. 实验中的key如果设置为IPC_PRIVATE,不同的进程间好像是不一样的,解决办法是设置为常数。

生产者代码:

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/shm.h>
#define N 4
#define size  N*1024+8

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};    //信号量集 

int main(){
	//记录缓冲区开始的位置和结束的位置
	int* start;
	int* end;
	   /*****申请共享内存空间*****/ 
    int shmid;
    char *viraddr;
    char buffer[1024];
    shmid=shmget(8888,size,0666|IPC_CREAT);
    viraddr=(char*)shmat(shmid,0,0);
    start = (int*)viraddr;//缓冲区开始有的位置 
    end = (int*)(viraddr+4);//缓冲区开始结束的位置 

	/*****定义PV操作*****/
	struct sembuf P,V;
    P.sem_num=0;
    P.sem_op=-1;
    P.sem_flg=SEM_UNDO;
    V.sem_num=0;
    V.sem_op=1;
    V.sem_flg=SEM_UNDO;
    /*****申请只有一个信号量的信号量集*****/
    int emptyid;
	int fullid;                                 //同步信号量
    int mutex;  //互斥信号量

 union semun arg;                                 
    if(semget(1000,1,IPC_CREAT|IPC_EXCL)!= -1){
	
    	emptyid=semget(1000,1,IPC_CREAT|0666);
   	 fullid=semget(2000,1,IPC_CREAT|0666);
  	  mutex=semget(3000,1,IPC_CREAT|0666);\
  	  	    /*****分别对每个信号量赋初值*****/
	   
	    arg.val=N;
	    semctl(emptyid,0,SETVAL,arg);
	    arg.val=0;
 	   	semctl(fullid,0,SETVAL,arg);
  	  	arg.val=1;
   		semctl(mutex,0,SETVAL,arg);
   		*end = *start=0;
   		puts("第一个生产者初始化\n");
	}else{
		printf("这不是第一个生产者\n");
		emptyid=semget(1000,1,0666);
   	 	fullid=semget(2000,1,0666);
  	  	mutex=semget(3000,1,0666);
	}
    

	int flag =0;
while(1){
	puts("1 生产产品,2 退出,3 撤销信号量集、释放共享内存");
	scanf("%d",&flag);
	getchar();
	switch(flag){
		case 1:
			start = (int*)viraddr;//缓冲区开始有的位置 
		    end = (int*)(viraddr+4);//缓冲区开始结束的位置 
			char* p = viraddr+8+(*end)*1024;//共享内存正文开始位置
			semop(emptyid,&P,1);
			semop(mutex,&P,1);

			puts("输入生产的信息:");
			fgets(buffer,1024,stdin);
			strcpy(p,buffer);
			*end = (*end+1)%N;
			semop(mutex,&V,1);
			semop(fullid,&V,1);
			printf("生产成功 start=%d  end=%d\n",*start,*end);
			break;
		case 2:
			printf("退出成功\n");
			exit(0);
			break;
		case 3:
			shmdt(viraddr);
	        /*****撤销信号量集、释放共享内存*****/
	        shmctl(shmid,IPC_RMID,0);
	        semctl(emptyid,IPC_RMID,0);
	        semctl(fullid,IPC_RMID,0);
	        semctl(mutex,IPC_RMID,0);
	        printf("撤销成功,退出\n");
	        exit(0);
			break;		
	} 		
}
	return 0;
} 

消费者代码:

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/shm.h>
#define N 4
#define size  N*1024+8


int main(){
	
	//记录缓冲区开始的位置和结束的位置
	int* start;
	int* end; 
		/*****申请只有一个信号量的信号量集*****/
    int emptyid;
	int fullid;                                 //同步信号量
    int mutex;                                  //互斥信号量 
	emptyid=semget(1000,1,0666);
   	fullid=semget(2000,1,0666);
  	mutex=semget(3000,1,0666);
	/*****定义PV操作*****/
	struct sembuf P,V;
    P.sem_num=0;
    P.sem_op=-1;
    P.sem_flg=SEM_UNDO;
    V.sem_num=0;
    V.sem_op=1;
    V.sem_flg=SEM_UNDO;

    /*****申请共享内存空间*****/ 
    int shmid;
    char *viraddr;
    char buffer[1024];
    shmid=shmget(8888,size,0666|IPC_CREAT);
    viraddr=(char*)shmat(shmid,0,0);
    
    int flag =0;
while(1){
	puts("1 消费产品,2 退出,3 撤销信号量集、释放共享内存");
	scanf("%d",&flag);
	getchar();
	switch(flag){
		case 1:
		    start = (int*)viraddr;//缓冲区开始有的位置 
		    end = (int*)(viraddr+4);//缓冲区开始结束的位置 
		    char* p = viraddr+8+(*start)*1024;
		    //    消费代码段 
		    	semop(fullid,&P,1);
			semop(mutex,&P,1);
			printf("消费的消息是: %s",p);
			*start = (*start+1)%N;
			printf(" start=%d ",*start);
			printf(" end=%d \n",*end);
			semop(mutex,&V,1);
			semop(emptyid,&V,1);
			break;
		case 2:
			printf("退出成功\n");
			exit(0);
			break;
		case 3:
		shmdt(viraddr);
	        /*****撤销信号量集、释放共享内存*****/
	        shmctl(shmid,IPC_RMID,0);
	        semctl(emptyid,IPC_RMID,0);
	        semctl(fullid,IPC_RMID,0);
	        semctl(mutex,IPC_RMID,0);
	        printf("撤销成功,退出\n");
	        exit(0);
			break;		
	} 		
}


}
	

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值