【操作系统】进程的控制和通信

简介: 【操作系统】进程的控制和通信


一. 实验目的

(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",&times);
  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机制,完成 “石头、剪子、布”的游戏。

目录
相关文章
|
1月前
|
算法 调度 UED
深入理解操作系统:进程调度与优先级队列
【10月更文挑战第31天】在计算机科学的广阔天地中,操作系统扮演着枢纽的角色,它不仅管理着硬件资源,还为应用程序提供了运行的环境。本文将深入浅出地探讨操作系统的核心概念之一——进程调度,以及如何通过优先级队列来优化资源分配。我们将从基础理论出发,逐步过渡到实际应用,最终以代码示例巩固知识点,旨在为读者揭开操作系统高效管理的神秘面纱。
|
24天前
|
消息中间件 安全 算法
深入理解操作系统:进程管理的艺术
【10月更文挑战第38天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是硬件与软件的桥梁,更是维持计算机运行秩序的守夜人。本文将带你走进操作系统的核心——进程管理,探索它是如何协调和优化资源的使用,确保系统的稳定与高效。我们将从进程的基本概念出发,逐步深入到进程调度、同步与通信,最后探讨进程安全的重要性。通过这篇文章,你将获得对操作系统进程管理的全新认识,为你的计算机科学之旅增添一份深刻的理解。
|
28天前
|
算法 调度 UED
深入理解操作系统:进程管理与调度策略
【10月更文挑战第34天】本文旨在探讨操作系统中至关重要的一环——进程管理及其调度策略。我们将从基础概念入手,逐步揭示进程的生命周期、状态转换以及调度算法的核心原理。文章将通过浅显易懂的语言和具体实例,引导读者理解操作系统如何高效地管理和调度进程,保证系统资源的合理分配和利用。无论你是初学者还是有一定经验的开发者,这篇文章都能为你提供新的视角和深入的理解。
42 3
|
1月前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
1月前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
1月前
|
算法 调度 UED
深入理解操作系统的进程调度机制
本文旨在探讨操作系统中至关重要的组成部分之一——进程调度机制。通过详细解析进程调度的概念、目的、类型以及实现方式,本文为读者提供了一个全面了解操作系统如何高效管理进程资源的视角。此外,文章还简要介绍了几种常见的进程调度算法,并分析了它们的优缺点,旨在帮助读者更好地理解操作系统内部的复杂性及其对系统性能的影响。
|
1月前
|
消息中间件 存储 供应链
进程间通信方式-----消息队列通信
【10月更文挑战第29天】消息队列通信是一种强大而灵活的进程间通信机制,它通过异步通信、解耦和缓冲等特性,为分布式系统和多进程应用提供了高效的通信方式。在实际应用中,需要根据具体的需求和场景,合理地选择和使用消息队列,以充分发挥其优势,同时注意其可能带来的复杂性和性能开销等问题。
|
1月前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
39 2
|
1月前
|
消息中间件 算法 Linux
深入理解操作系统之进程管理
【10月更文挑战第30天】在数字时代的浪潮中,操作系统作为计算机系统的核心,扮演着至关重要的角色。本文将深入浅出地探讨操作系统中的进程管理机制,从进程的概念入手,逐步解析进程的创建、调度、同步与通信等关键过程,并通过实际代码示例,揭示这些理论在Linux系统中的应用。文章旨在为读者提供一扇窥探操作系统深层工作机制的窗口,同时激发对计算科学深层次理解的兴趣和思考。
|
29天前
|
消息中间件 算法 调度
深入理解操作系统:进程管理的艺术
【10月更文挑战第33天】本文旨在揭示操作系统中进程管理的神秘面纱,带领读者从理论到实践,探索进程调度、同步以及通信的精妙之处。通过深入浅出的解释和直观的代码示例,我们将一起踏上这场技术之旅,解锁进程管理的秘密。
27 0