【操作系统】进程的控制和通信
文章目录
- 一. 实验目的
- 二. 实验内容
- 三. 实验步骤
- 四. 实验结果
一. 实验目的
(1)加深对进程概念的理解,进一步认识并发执行的实质。
(2)掌握Linux 操作系统中进程的创建和终止操作。
(3)理解进程间通信的概念和方法。
(4)掌握常用的Linux 进程间通信的方法。
二. 实验内容
(1)编写一个C程序,并使用系统调用fork()创建一个子进程。要求如下:
① 在子进程中分别输出当前进程为子进程的提示、当前进程的PID 和父进程的PID、根据用户输入确定当前进程的返回值、退出提示等信息。
② 在父进程中分别输出当前进程为父进程的提示、当前进程的PID 和子进程的PID、等待子进程退出后获得的返回值、退出提示等信息。
(2)编写C程序,使用Linux中的IPC机制,完成 “石头、剪子、布”的游戏。
三. 实验步骤
(1)编写一个C程序,并使用系统调用fork()创建一个子进程。
程序设计
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
pid_t childpid;
int retval;
int status;
childpid=fork();
if(childpid==0) // 子进程
{
printf("CHILD: I am the child process\n");
printf("CHILD: My PID: %d\n", getpid());
printf("CHILD: My parent's PID: %d\n", getppid());
printf("CHILD: The value of fork return is: %d\n", childpid);
printf("CHILD: Sleep for 1 second ..\n");
sleep(1);
printf("CHILD: Enter an exit value (0~255):");
scanf("%d",&retval);
printf("CHILD: Goodbye!\n");
exit(retval);
}
else if(childpid>0) // 父进程
{
printf("PARENT: I am the parent process\n");
printf("PARENT: My PID: %d\n", getpid());
printf("PARENT: my child's PID:%d\n", childpid);
printf("PARENT: I will now wait for my child to exit.\n");
wait(&status);
printf("PARENT: child's exit code is:%d\n",WEXITSTATUS(status));
printf("PARENT: Goodbye\n");
exit(0);
}
else // 错误处理
{
perror("fork error\n");
exit(0);
}
return 0;
}
程序分析
该程序展示了一个父子进程之间的通信方式。它使用了fork()函数创建了子进程并在子进程中输出一些信息,最后子进程通过exit()函数返回一个退出值。在父进程中,它等待子进程的退出并通过wait()函数获取子进程退出的状态码。
首先是fork()函数的调用,它会返回一个值,该值有不同的含义:
- 返回值为0时,表示当前进程是子进程。
- 返回值大于0时,表示当前进程是父进程,返回值为子进程的PID。
- 返回值小于0时,表示创建进程失败。
在子进程中,它会输出一些信息,包括它自己的PID和父进程的PID,以及fork()函数的返回值。然后,它会等待1秒钟,让父进程有时间执行并输出信息。接下来,它通过scanf()函数读取一个退出值,并通过exit()函数返回。
在父进程中,它会输出自己的PID和子进程的PID,并等待子进程退出。它通过wait()函数获取子进程退出时的状态码。wait()函数会阻塞当前进程,直到任意一个子进程退出。如果不关心哪个子进程退出,可以使用wait(NULL)。在该程序中,使用了&status来获取子进程退出的状态码,并通过WEXITSTATUS(status)函数获取子进程通过exit()函数返回的退出值。
最后,父进程输出子进程的退出值,并通过exit()函数退出进程。
总的来说,该程序展示了fork()函数创建子进程、子进程的输出、父进程等待子进程退出和获取子进程退出的状态码等基本操作。
(2)编写C程序,使用Linux中的IPC机制,完成 “石头、剪子、布”的游戏。
程序设计
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
struct Game
{
int Round;
long Type;
};
void result_send(int num)
{
struct Game game;
game.Type=1;
game.Round= rand()%3;
int ret=-1;
ret=msgsnd(num,&game,sizeof(int),0);
printf("%d msgsnd %d return %d\n",num,game.Round,ret);
}
int result_announce(int a,int b)
{
if ((a+1==b) ||(a-2==b))
return -1;
else if(a==b)
return 0;
else
return 1;
}
void writefile(int *result_list,int len)
{
int count_A=0;
int count_B=0;
int pingju=0;
FILE *fin;
fin=fopen("result.txt","w");
if(fin==NULL)
printf("This file wasn't opened");
int i;
for(i=0;i<len;i++)
{
switch(result_list[i])
{
case -1:
count_A++;
fprintf(fin,"No.%d: A win\n",i+1);
printf("No.%d: A win\n",i+1);
break;
case 0:
pingju++;
fprintf(fin,"No.%d: end in a draw\n",i+1);
printf("No.%d: end in a draw\n",i+1);
break;
case 1:
count_B++;
fprintf(fin,"No.%d: B win\n",i+1);
printf("No.%d: B win\n",i+1);
break;
}
}
printf("\nThe final result is A win:%ds \nB win:%ds \nend in a draw %ds\n",count_A,count_B,pingju);
fprintf(fin,"\nThe final result is A win: %ds \nB win: %ds \nend in a draw %ds\n",count_A,count_B,pingju);
fclose(fin);
}
int main()
{
int times;
int key1=1234;
int key2=5678;
int *result_list;
pid_t pid1,pid2;
int msgid1,msgid2;
msgid1=msgget(key1,IPC_CREAT | 0666);
msgid2=msgget(key2,IPC_CREAT | 0666);
if(msgid1==-1)
{
fprintf(stderr,"failed with error");
exit(EXIT_FAILURE);
}
if(msgid2==-1)
{
fprintf(stderr,"failed with error");
exit(EXIT_FAILURE);
}
printf("Game start, please input rounds:");
scanf("%d",×);
printf("times=%d\n",times);
result_list=(int*)malloc(times*sizeof(int));
int i;
for(i=0;i<times;i++)
{
printf("********i=%d********\n",i);
pid1=fork();
if(pid1==0)
{
//printf("in pid1:\n");
int pid=getpid();
printf("pid of pid1 is %d\n",pid);
srand((unsigned)time(0)*3000);
result_send(msgid1);
exit(11);
}
pid2=fork();
if(pid2==0)
{
//printf("in pid2:\n");
int pid=getpid();
printf("pid of pid2 is %d\n",pid);
srand((unsigned)time(NULL)*i);
result_send(msgid2);
exit(22);
}
if(pid1<0 || pid2<0)
{
fprintf(stderr,"Fork Failed");
printf("Fork failed!!");
exit(-1);
}
else
{
//printf("in else before wait:\n");
int status1,status2;
wait(&status1);
//printf("wait1 ok, status1=%d\n",WEXITSTATUS(status1));
wait(&status2);
//printf("wait2 ok, status2=%d\n",WEXITSTATUS(status2));
struct Game game1,game2;
msgrcv(msgid1,&game1,sizeof(game1)-sizeof(long),0,0);
printf("%d rcv %d ok\n",msgid1,game1.Round);
msgrcv(msgid2,&game2,sizeof(game2)-sizeof(long),0,0);
printf("%d rcv %d ok\n",msgid2,game2.Round);
int j=result_announce(game1.Round,game2.Round);
result_list[i]=j;
printf("result_announce is %d.\n",j);
}
}//end for
writefile(result_list,times);
if(msgctl(msgid1,IPC_RMID,0)==-1)
fprintf(stderr,"msgctl(IPC_RMID) failed\n");
if(msgctl(msgid2,IPC_RMID,0)==-1)
fprintf(stderr,"msgctl(IPC_RMID) failed\n");
exit(EXIT_SUCCESS);
}
程序分析
该程序是一个石头剪刀布游戏的模拟,通过使用进程间通信机制实现了两个进程间的交互。程序创建了两个消息队列,分别用于进程1和进程2向其父进程发送消息,父进程接收消息并比较得出游戏结果,然后输出到文件中。以下是程序的详细分析:
首先定义了一个结构体Game,用于存储比赛的轮数和玩家出的手势,可以看作一个消息的格式。游戏结果用一个int型数组result_list来存储,其中0表示平局,-1表示A胜利,1表示B胜利。然后是result_send函数,它用于向指定的队列发送一条Game类型的消息。随机产生一个0~2的整数表示玩家出的手势,并设置Type为1表示这是来自进程1的消息。返回值表示发送操作是否成功。
result_announce函数用于比较两个玩家出的手势,返回值为0表示平局,-1表示A胜利,1表示B胜利。
writefile函数用于将游戏结果写入文件result.txt中,同时输出到控制台。其中count_A、count_B、pingju分别表示A胜利的次数、B胜利的次数和平局的次数。
在主函数中,首先创建了两个消息队列,然后询问玩家要进行多少轮游戏,并分配result_list数组的大小。进入for循环后,创建了两个子进程pid1和pid2,分别代表进程1和进程2。
在子进程中,通过调用srand()和result_send()函数来随机选择手势,然后向父进程所绑定的消息队列中发送一条消息。为了让每次产生的随机数不同,可以使用时间函数time()和进程id组合生成随机数的种子。
在父进程中,首先通过wait()函数等待子进程1和子进程2都结束,并获取它们退出时的状态值。然后使用msgrcv()函数从消息队列中接收子进程所发出的消息,也就是两个玩家出的手势。
接下来,调用result_announce()函数比较两个玩家出的手势,获取比赛结果,并将得到的结果保存到result_list数组中。
当所有的比赛都结束后,调用writefile()函数来输出结果到result.txt文件中,并输出到控制台。同时,通过msgctl()函数删除之前创建的两个消息队列。
总的来说,该程序运用了fork()函数创建子进程、消息队列实现进程间通信等基本操作。它模拟了石头剪刀布游戏的过程,同时也展示了使用进程间通信机制进行协作的思路。
四. 实验结果
(1)编写一个C程序,并使用系统调用fork()创建一个子进程。
(2)编写C程序,使用Linux中的IPC机制,完成 “石头、剪子、布”的游戏。