一、实验时间

2021年4月22日,星期四,第8周

二、实验内容

通过Linux 系统中管道通信机制,加深对于进程通信概念的理解,观察和体验并发进程间的通信和协作的效果 ,练习利用无名管道进行进程通信的编程和调试技术。

管道pipe是进程间通信最基本的一种机制,两个进程可以通过管道一个在管道一端向管道发送其输出,给另一进程可以在管道的另一端从管道得到其输入。管道以半双工方式工作,即它的数据流是单方向的。因此使用一个管道一般的规则是读管道数据的进程关闭管道写入端,而写管道进程关闭其读出端。

关键:利用pipe系统调用
在这里插入图片描述

三、示例实验

这个是实验指导书上的示例实验,关键要理解以下几点

  1. pipe系统调用的使用和声明
  2. 如何使用read方法和write方法读取和向发送另一个进程发送消息
  3. 注意close方法的使用,在发送消息或读取消息的时候管道的另一个端口需要关闭
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    int pid; //进程号
    int pipe1[2]; //存放第一个无名管道标号
    int pipe2[2]; //存放第二个无名管道标号
    int x; // 存放要传递的整数
    //使用pipe()系统调用建立两个无名管道。建立不成功程序退出,执行终止
    if(pipe(pipe1) < 0)
    {
        perror("pipe not create");
        exit(EXIT_FAILURE);
    }
    if(pipe(pipe2) < 0)
    {
        perror("pipe not create");
        exit(EXIT_FAILURE);
    }
    //使用fork()系统调用建立子进程,建立不成功程序退出,执行终止
    if((pid=fork()) <0)
    {
        perror("process not create");
        exit(EXIT_FAILURE);
    }
    //子进程号等于0 表示子进程在执行
    else if(pid == 0)
    {
        //子进程负责从管道1的0端读,管道2的1端写
        //所以关掉管道1的1端和管道2的0端。
        close(pipe1[1]);
        close(pipe2[0]);
        //每次循环从管道1 的0 端读一个整数放入变量X 中,
        //并对X 加1后写入管道2的1端,直到X大于10
        do
        {
            read(pipe1[0],&x,sizeof(int));
            printf("child %d read: %d\n",getpid(),x++);
            write(pipe2[1],&x,sizeof(int));
        }while( x<=9 );
        //读写完成后,关闭管道
        close(pipe1[0]);
        close(pipe2[1]);
        //子进程执行结束
        exit(EXIT_SUCCESS);
    }
    //子进程号大于0 表示父进程在执行
    else
    {
        //父进程负责从管道2的0端读,管道1的1端写,
        //所以关掉管道1 的0 端和管道2 的1端。
        close(pipe1[0]);
        close(pipe2[1]);
        x=1;
        //每次循环向管道1 的1 端写入变量X 的值,并从
        //管道2的0 端读一整数写入X 再对X加1,直到X 大于10
        do
        {
            write(pipe1[1],&x,sizeof(int));
            read(pipe2[0],&x,sizeof(int));
            printf("parent %d read: %d\n",getpid(),x++);
        }while(x<=9);
        //读写完成后,关闭管道
        close(pipe1[1]);
        close(pipe2[0]);
    }
    //父进程执行结束
    return EXIT_SUCCESS;
}

输出结果
在这里插入图片描述
在做本次实验之前最好理解清楚这个示例实验,里面具体的方法要求掌握

四、本次实验

实验内容

在这里插入图片描述

在这次实验的过程中我遇到了几个比较坑的地方,困扰了不少时间

  1. 这次实验中遇到了父进程创建两个子进程的问题,一开始的时候我没有调控好,在创建进程的时候遇到了很多问题,比如父进程已经执行完了,第二个子进程还在执行,后来在参考了别人的代码之后发现在判断if(pid == 0),else的时候要注意,两个进程不能并列判断
  2. 关于read方法和write的注意事项:起初我采用从管道的0端写,1端读,结果读出来的结果是随机数,很奇怪。后来我改成了从0端读,1端读,问题就顺利解决了

以下是这次实验的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
//f(x)
int fx(int x)
{
    if(x <= 0)
    {
        printf("the number you input is wrong!");
        return 0;
    }
    else if(x == 1)
    {
        return 1;
    }
    else if(x > 1)
    {
        return fx(x-1) * x;
    }
}
//f(y)
int fy(int y)
{
    if(y <= 0)
    {
        printf("the number you input is wrong!");
        return 0;
    }
    else if(y == 1 || y == 2)
    {
        return 1;
    }
    else if(y > 2)
    {
        return fy(y-1) + fy(y-2);
    }
}
//f(x,y)
int fxy(int fx, int fy)
{
    return fx + fy;
}
int main()
{
    /*
        建立三个进程,一个进程负责计算f(x)的值,一个负责计算f(y)的值,另一个负责计算f(x,y)的值
        f(x),f(y)的值通过管道传输
    */
    //建立两个管道
    int pipe1[2];
    int pipe2[2];
    //两个子进程的进程号
    int pid1;
    int pid2;
    //数值x和y
    int x;
    int y;
    //从键盘输入x和y
    printf("please input number x: ");
    scanf("%d",&x);
    printf("\n");
    printf("please input number y: ");
    scanf("%d",&y);
    printf("\n");
    //保存f(x)和f(y)的变量
    int temp1,temp2;
    //使用pipe()系统调用建立两个无名管道。建立不成功程序退出,执行终止
    if(pipe(pipe1) < 0)
    {
        perror("pipe not create");
        exit(EXIT_FAILURE);
    }
    if(pipe(pipe2) < 0)
    {
        perror("pipe not create");
        exit(EXIT_FAILURE);
    }
    pid1 = fork();
    //使用fork()系统调用建立子进程,建立不成功程序退出,执行终止
    if(pid1 <0)
    {
        perror("process not create");
        exit(EXIT_FAILURE);
    }
    //第一个子进程,pipe1[1]用来写
    if(pid1 == 0)
    {
        //关掉pipe1[0]端
        close(pipe1[0]);
        //计算f(x)
        temp1 = fx(x);
        printf("pid1 f(x) = %d\n",temp1);
        //发送消息
        write(pipe1[1],&temp1,sizeof(int));
        //发送完成后关闭管道
        close(pipe1[1]);
    }
    else
    {
        close(pipe1[1]);
        //读从管道1获得的数值
        read(pipe1[0],&temp1,sizeof(int));
        close(pipe1[0]);
        pid2 = fork();
        //使用fork()系统调用建立子进程,建立不成功程序退出,执行终止
        if(pid2 <0)
        {
            perror("process not create");
            exit(EXIT_FAILURE);
        }
        //第二个子进程,pipe2[1]用来写
        if(pid2 == 0)
        {
            //关掉pipe2[0]端
            close(pipe2[0]);
            //计算f(y)
            temp2 = fy(y);
            printf("pid2 f(y) = %d\n",temp2);
            //发送消息
            write(pipe2[1],&temp2,sizeof(int));
            close(pipe2[1]);
        }
        //父进程
        else
        {
            wait(NULL);
            close(pipe2[1]);
            //接受第二个子进程从管道里发来的信息
            read(pipe2[0],&temp2,sizeof(int));
            int result = fxy(temp1,temp2);
            printf("f(x) = %d\n",temp1);
            printf("f(y) = %d\n",temp2);
            printf("f(x,y) = %d\n",result);
            //读完成后关闭管道
            close(pipe2[1]);
            //父进程执行结束
            return EXIT_SUCCESS;
        }
    }
}

输入x=1,y=2的输出结果
在这里插入图片描述
输入x=5,y=5的输出结果
在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐