【实验内容】
- 编写C程序,使用Linux操作系统中的信号量机制模拟解决经典的进程同步问题;生成者-消费者问题。假设有一个生成者和一个消费者,缓冲区可以存放产品,生成者不断生产产品并存入缓冲区,消费者不断从缓冲区中取出产品并消费。
- 编写C程序,使用Linux中的IPC机制完成“石头、剪刀、布”游戏。使之能够在网络上运行。
【实验目的】
- 加强对进程同步和互斥的理解,学会使用信号量解决资源共享问题;
- 熟悉Linux进程同步原语;
- 掌握信号量wait/signal原语的使用方法,理解信号量的定义、赋初值及wait/signal操作;
- 理解进程间通信的概念和方法;
- 掌握常用的Linux进程间通信的方法。
【实验技术】
1.VMware Workstation
版本:15.5.0 build-14665864
作用:可在一部实体机器上模拟完整的网络环境,以及可便于携带的虚拟机器,在虚拟网路,实时快照,拖曳共享文件夹,支持 PXE 等方面的特点使它成为必不可少的工具。
2.Ubuntu系统
版本:Ubuntu20.04
作用: Ubuntu是一个基于Debian的以桌面应用为主的Linux操作系统, Ubuntu的目标在于为一般用户提供一个最新同时又相当稳定,主要以自由软件建构而成的操作系统。Ubuntu目前具有庞大的社区力量支持,用户可以方便地从社区获得帮助。
【实验原理/思路】
- 针对实验内容(1),可以创建三个进程,其中一个进程为裁判进程,另外两个进程为选手进程。可将“石头”“剪刀”“布”这三招定义为三个整型值,胜负关系为:石头>剪刀>布>石头。
- 选手进程按照某种策略(如随机产生)出招,然后交给裁判进程判断大小。裁判进程将对手的出招和胜负结果通知选手。比赛采取多轮(如100轮)定胜负机制,并由裁判宣布最后结果。每次出招由裁判限定时间,超时则判负。
- 每一轮的胜负结果可以存放在文件或其他数据结构中。比赛结束后,打印每一轮的胜负情况和总的结果。具体的实验步骤如下。
(1)设计表示“石头”“剪刀”“布”的数据结构以及它们之间的大小规则;
(2)设计比赛结果的存放方式;
(3)选择IPC方法;
(4)根据所选择的IPC方法,创建对应的IPC 资源;
(5)完成选手进程;
(6)完成裁判进程。
【实验步骤与实验结果】
一、生成者-消费者问题
1.源代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 5
int queue[NUM];
sem_t sem_blank,sem_food;//空盘子,包子
void producer(){int i=0;
while (1){ sem_wait(&sem_blank);
queue[i]=rand()%100+1;
sem_post(&sem_food);
printf("producer queue[%d] is : %d\n",i,queue[i]);
i++;
i=i%5;
sleep(rand()%3);
}}
void consumer(){
int i=0;
while (1){
sem_wait(&sem_food);
printf("consumer queue[%d] is %d\n",i,queue[i]);
queue[i]=0;
sem_post(&sem_blank);
i++;
i=i%5;
sleep(rand()%3); }}
int main(void){
int ret;
ret=sem_init(&sem_blank,0,5);//value值就表示桌子上有几个盘子
if (ret==-1){ perror("[sem_blank]");
return -1; }
ret=sem_init(&sem_food,0,0);//value值表示桌子上有几个包子
if (ret==-1){ perror("[sem_food]");
return -1; }
//创建两个线程,生产者,消费者
pthread_t tid1,tid2;
while ((pthread_create(&tid1,NULL,(void *)producer,NULL))!=0);
while ((pthread_create(&tid2,NULL,(void *)consumer,NULL))!=0);
//回收线程
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
}
2.运行结果
二、“石头、剪刀、布”游戏
1.源代码
#include <unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/wait.h>
int judge(char a,char b);
int main(void)
{
int fd1,fd2,fd3,fd4,i=0;
int status;
char * myfifo1 = "/tmp/myfifo1";
char * myfifo2 = "/tmp/myfifo2";
char * myfifo3 = "/tmp/myfifo3";
char * myfifo4 = "/tmp/myfifo4";
char start[1] = {'3'};
char end[1] = {'4'};
char c1[1];//用来存放p1发送的消息
char c2[2];//用来存放p2发送的消息
char s1[1];//用来存放裁判发送的消息
char s2[2];//用来存放裁判发送的消息
int p1w=0,p2w=0,pd=0;
if((mkfifo(myfifo1,0666)<0)&&(errno!=EEXIST))
{
printf("cannot create fifo.\n");
exit(1);
}
if((mkfifo(myfifo2,0666)<0)&&(errno!=EEXIST))
{
printf("cannot create fifo.\n");
exit(1);
}
if((mkfifo(myfifo3,0666)<0)&&(errno!=EEXIST))
{
printf("cannot create fifo.\n");
exit(1);
}
if((mkfifo(myfifo4,0666)<0)&&(errno!=EEXIST))
{
printf("cannot create fifo.\n");
exit(1);
}
pid_t p1 = fork();
/*********************************/
if(p1==0)
{
srand(time(NULL));
while(1)
{
i++;
fd1 = open(myfifo1,O_RDONLY);
read(fd1,s1,1);
close(fd1);
if(s1[0] == '3')
{
fd3 = open(myfifo3,O_WRONLY);
int j = rand()%3;
s1[0] = j + '0';
write(fd3,s1,strlen(s1)+1);
close(fd3);
}else if(s1[0] == '4'){
exit(0);
}
}
return 0;
}
pid_t p2=fork();
/*********************************/
if(p2==0)
{
srand(time(NULL)+100);
while(1)
{
i++;
fd2 = open(myfifo2,O_RDONLY);
read(fd2,s2,1);
close(fd2);
if(s2[0] == '3')
{
fd4 = open(myfifo4,O_WRONLY);
int j = rand()%3;
s2[0] = j + '0';
write(fd4,s2,strlen(s2)+1);
close(fd4);
}else if(s2[0] == '4'){
exit(0);
}
}
return 0;
}
//比赛场地
while(1)
{
if(p1 != 0 && p2!=0){
i++;
if(i>100)
{
fd1 = open(myfifo1,O_WRONLY);
fd2 = open(myfifo2,O_WRONLY);
write(fd1,end,strlen(start)+1);
write(fd2,end,strlen(start)+1);
close(fd1);
close(fd2);
break;
}
printf("这是第 %d 轮\n",i);
fd1 = open(myfifo1,O_WRONLY);
fd2 = open(myfifo2,O_WRONLY);
//向管道1,2中写入开始出拳信号start
write(fd1,start,strlen(start+1));
write(fd2,start,strlen(start+1));
close(fd1);
close(fd2);
sleep(1);
fd3 = open(myfifo3,O_RDONLY);
fd4 = open(myfifo4,O_RDONLY);
//从管道3,4中读出比赛选手出拳情况
read(fd3,c1,sizeof(c1));
read(fd4,c2,sizeof(c2));
//打印一轮结果
printf("p1: %c\n",c1[0]);
printf("p2: %c\n",c2[0]);
close(fd3);
close(fd4);
int tmp = judge(c1[0],c2[0]);
if(tmp==0)
{
printf("in a draw!\n");
pd++;//平局
}
else
{
printf("%s wins!\n",(tmp>0)?"p1":"p2");
if(tmp>0){
p1w++;//p1胜
}
else {
p2w++; //p2胜
}
}
}
}
//打印最终统计结果
printf("In summary:\n");
printf("p1 wins %d rounds.\n",p1w);
printf("p2 wins %d rounds.\n",p2w);
printf("%d rounds end in a draw.\n",pd);
printf("%s wins in the game!\n",(p1w>p2w)?"p1":"p2");
}
//0——石头,1——剪刀,2——布
int judge(char a,char b)//规定游戏判定规则
{
int r=0;
if(a==b){
r=0;}
else
{
if(a=='0'&&b=='1')r=1;
if(a=='0'&&b=='2')r=-1;
if(a=='1'&&b=='2')r=1;
if(a=='1'&&b=='0')r=-1;
if(a=='2'&&b=='0')r=1;
if(a=='2'&&b=='1')r=-1;
}
return r;
}
2.运行结果
【实验小结】
在这次实验的过程中,通过学习gcc编译全过程,了解到linux系统编译c语言和win10编译的差异,通过总结的动手和尝试,深刻了解到如何去编译和运行c语言代码。C语言编程是计算机编程学习的基本技能, 在利用C语言编程时可以将C语言和Linux系统结合起来, 掌握Linux系统、C语言编程的各类技巧, 才能熟练掌握和应用两种技术, 提高程序编写质量和效率。更加了解Linux一些常用指令的操作以及其作用,对于一个刚开始接触lniux操作系统的初学者来说非常有用,有助于以后能够更进一步学习Linux操作系统。