问题描述:针对的是操作系统中的生产者-消费者问题,方式是在不同计算机间实现(这里实在linux下实现的)
涉及的主要知识有网络间通信、信号量和pv操作;
agui1_2produce.c一台计算机中运行,agui1_2consume.c可以在其他计算机中运行 (注:一台计算机也是可以的)
编译命令 :gcc agui1_2produce.c -o agui1_2produce -lpthread
gcc agui1_2consume.c -o agui1_2consume -lpthread
运行命令:假设有两台计算机:computerA,computerB
1.在computerA(假设ip地址是192.168.10.57)下运行:./agui1_2produce 8888
2.在computerB下运行::./agui1_2consume 192.168.10.57 8888
当只有一台计算机时:运行上面两个命令时,可以把ip地址改成127.0.0.1
查看ip的命令ip -s addr
实验结果:可以比较生成者的生成和消费者的消费是否满足生产者-消费者问题的要求
下面是实验代码,更加详细的实验代码和实验结果描述在: http://pan.baidu.com/s/1dDs3uHb
agui1_2produce.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/sem.h>
#include<signal.h>
#define Maxbuf 10//存放产品的缓冲区的大小
struct Circlebuf{
int read; //读取产品的位置
int write;//存放产品的位置
int buf[Maxbuf];//存放产品的数据空间
} circlebuf;
//向 Circlebuf *c中写入 *value的值
//即使用中想circlebuf中存放产品,注意点有write的加一
void writeCirclebuf(struct Circlebuf*c,int *value){
c->buf[c->write]=*value;
c->write=(c->write+1)%Maxbuf;
}
//从Circlebuf *c中取出产品
//注意的是read的加一
int readCirclebuf(struct Circlebuf* c){
int value;
value=c->buf[c->read];
c->buf[c->read]=0;
c->read=(c->read+1)%Maxbuf;
return value;
}
//遍历一下Circlebuf*c中产品的数值,不改变read write 的值
void overCirclebuf(struct Circlebuf*c){
int i;
printf("********************缓冲区各单元的值:");
for(i=0;i<Maxbuf;i++){
printf("%d ",c->buf[i]);
}
printf("\n");
}
#define SEM_KEY 4009
struct sembuf semaphore;
int semid;
//信号量的初始化
//初始化三个信号量
int initSembuf(){
int sem=0;
if((semid=semget(SEM_KEY,4,IPC_CREAT|0666))>=0){
sem=1;
semctl(semid,0,SETVAL,sem); //第一个信号量 mutex 初始值1
sem=Maxbuf;
semctl(semid,1,SETVAL,sem);//第二个信号量 empth 初始值10
sem=0;
semctl(semid,2,SETVAL,sem);//第三个信号量 full 初始值 0
printf("initsembuf success!!\n");
return 1;
}else{
return 0;
}
}
void pmutex(){
semaphore.sem_num=0;
semaphore.sem_op=-1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
void vmutex(){
semaphore.sem_num=0;
semaphore.sem_op=1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
void pempty(){
semaphore.sem_num=1;
semaphore.sem_op=-1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
void vempty(){
semaphore.sem_num=1;
semaphore.sem_op=1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
void pfull(){
semaphore.sem_num=2;
semaphore.sem_op=-1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
void vfull(){
semaphore.sem_num=2;
semaphore.sem_op=1;
semaphore.sem_flg=SEM_UNDO;
semop(semid,&semaphore,1);
}
//删除信号量
void sigend(int sig){
semctl(semid,IPC_RMID,0);
exit(0);
}
//生产者
//注意的是while(1)循环 可以改成循环30次
//只要保证问题能够说明即可,实际中不会使用那么多循环的,否则浪费端口资源
void *produce(void *i){
int *n=i;
while(1){
pempty();
pmutex();
writeCirclebuf(&circlebuf,n);
printf("+++++++++++++++生产者%d写入缓冲区的值=%d\n",*n,*n);
overCirclebuf(&circlebuf);
int j;
for(j=0;j<50000000;j++){
}
vmutex();
vfull();
}
}
//消费者
//这个函数在是消费者线程中实现的
//目的是向发起链接的进程发送产品,每次发送12个产品
//发送的方式是使用文件描述符,这个文件描述符是从TCP通信的accept函数中得到的(参数*temp就是那个文件描述符)
void *consume(void *temp){
int *newsockfd=temp;
int i;
char str[100];
char receivedNum[100];
int nbytes;
int num;
if((nbytes=read(*newsockfd,receivedNum,100))==-1){
printf("read error:%s\n",strerror(errno));
exit(1);
}
if((num=atoi(receivedNum))<0){
puts("receivedNum error!!");
exit(1);
}
int value=0;
for(i=0;i<num;i++){
pfull();
pmutex();
printf("||| one |||");
value=readCirclebuf(&circlebuf);
printf("--------------消费者取走的产品值为:%d\n",value);
memset(str,0,sizeof(str));
if(0!=value){
sprintf(str,"%d",value);
if(write(*newsockfd,str,sizeof(str))==-1){
printf("write error:%s\n",strerror(errno));
exit(1);
}
}else{
puts("none");
}
vmutex();
vempty();
}
close(*newsockfd);
}
int main(int argc,char *argv[]){
//网络通信准备
// 主要步骤是socket(),bind(),listen(),另外accept()是取得申请者的文件描述符,如果没有申请进程,那么运行到那里时会阻塞,直到有申请进程发起链接
int portnumber;//生产者使用的端口号
int sockfd,newsockfd;//文件描述符,sockfd是服务的使用的,newsockfd是针对申请进程的文件描述符,
struct sockaddr_in server_addr,client_addr;
int addr_len=sizeof(struct sockaddr_in);
if(argc!=2){
puts("argument error!!");
exit(1);
}
//把argv[1]字符串转换成数值(端口号)
if((portnumber=atoi(argv[1]))<0){
puts("portnumber error!!");
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
printf("socket error:%s\n",strerror(errno));
exit(1);
}
printf("create server socket success!! socket id:%d\n",sockfd);
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))==-1){
printf("bind error:%s\n",strerror(errno));
exit(1);
}
printf("bind port success!! local port:%d\n",portnumber);
if(listen(sockfd,10)==-1){
printf("listen error:%s\n",strerror(errno));
exit(1);
}
printf("listening....\n");
//pv操作准备
while(!initSembuf());
signal(SIGINT,sigend);
signal(SIGTERM,sigend);
//pro
int i;
pthread_t proid;
printf("进行生产\n");
int *addr_data=(int*)malloc(22*sizeof(int));
//开启了20个生产者线程,其中addr_data[]是传送给produce()函数的参数
for(i=1;i<21;i++){
addr_data[i-1]=i;
pthread_create(&proid,NULL,produce,&addr_data[i-1]);
}
//consu
//不断的接受申请进程的链接
//接受的时候创建消费者线程,取产品发送给申请进程
//没有申请进程发起链接时,accept()阻塞,等待申请
while(1){
sleep(4);
puts("begin accept:::::");
if((newsockfd=accept(sockfd,(struct sockaddr*)(&client_addr),&addr_len))==-1){
printf("accept error:%s\n",strerror(errno));
exit(1);
}
printf("server get connection from:%s %d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
pthread_t id;
//newsockfd是针对申请进程的文件描述符
pthread_create(&id,NULL,consume,&newsockfd);
for(i=0;i<5000000;i++){
}
sleep(3);
}
sleep(8);
close(sockfd);
return 0;
}
agui1_2consume.c
/*
编译时带上-lpthread 例如:gcc pcwNew.c -o pcwNew -lpthread
使用方式 : ./pcwNew localhost 8888
(localhost 是你要访问的服务机 8888是服务机端口号)
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netdb.h>
int main(int argc,char*argv[]){
int sockfd;
struct hostent *hostname;
int portnumber;
struct sockaddr_in server_addr;
char buffer[1024];
if(argc!=3){
puts("argument error!!");
exit(1);
}
//获取主机
if((hostname=gethostbyname(argv[1]))==NULL){
printf("get hostname error!!");
exit(1);
}
//获取要访问的端口号
if((portnumber=atoi(argv[2]))<0){
printf("portnumber error!!");
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
printf("create socket error:%s\n",strerror(errno));
exit(1);
}
printf("create client socket success! socket id:%d\n",sockfd);
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr*)hostname->h_addr);
//获取文件描述符
if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))==-1){
printf("conncet error:%s\n",strerror(errno));
exit(1);
}
int nbytes;
int i=0;
char charnums[100];
puts("please input how much products you want:");
scanf("%s",charnums);
int num;
if((num=atoi(charnums))<0){
puts("charnums error");
exit(1);
}
if(write(sockfd,charnums,sizeof(charnums))==-1){
printf("write error:%s\n",strerror(errno));
exit(1);
}
for(i=0;i<num;i++){
if((nbytes=read(sockfd,buffer,100))==-1){
printf("read error:%s\n",strerror(errno));
exit(1);
}
buffer[nbytes]='\0';
printf("received:%s\n",buffer);
int j;
for(j=0;j<500000;j++){
}
sleep(1);
}
close(sockfd);
return 0;
}