• linux c编程:管道


    2在前面介绍过,进程之间交换信息的唯一途径就是传送打开的文件。可以经由fork或者exec来传送。这一章将介绍新的进程共享方式
    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信,如下图所示


     
    不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe就是提供这份公共资源的形式的一种。
    管道是由调用pipe函数来创建
    #include <unistd.h>
    int pipe (int fd[2]);
    fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
     
    管道是如何实现进程间的通信
    (1)父进程创建管道,得到两个⽂件描述符指向管道的两端
     
    (2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
     
    (3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管读写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。  
     
     
    实现如下图所示


     
    实现代码
    int chapter15_5()
    {
        int n;
        int fd[2];
        pid_t pid;
        char line[MAXLINE];
        if (pipe(fd) < 0)
        {
            printf("pipe error");
        }
        pid=fork();
        if (pid == 0)
        {
            close(fd[1]);
            n=read(fd[0],line,MAXLINE);
            write(STDOUT_FILENO,line,n);
        }
        else
        {
            close(fd[0]);
            write(fd[1],"hello world ",12);
        }
    }
    在上面的代码中,是父进程关闭读通道,子进程关闭写通道。父进程向fd[1]中写入数据,子进程从fd[0]中读出数据并显示在终端上。同样的我们也可以子进程关闭读通道,父进程关闭写通道,由子进程来写入数据,父进程读取数据
     
    管道读取数据的4种情况:
    1 读端不读,写端一直写

    写端不写,但是读端一直读

    3 读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。 

    如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。

    4 读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。

    来看下这种异常的代码:

    int chapter_15_5_1()

    {

        int fd[2];

        int ret = pipe(fd);

        if (ret == -1)

        {

            printf("pipe error ");

            return 1;

        }

        pid_t id = fork();

        if (id == 0)

        {

            int i = 0;

            close(fd[0]);

            char child[20] = "I am a student";

            while (i<10)

            {

                write(fd[1], child, strlen(child) + 1);

                sleep(2);

                i++;

            }

        }

        else if (id>0)

        {

            close(fd[1]);

            char msg[100];

            int status = 0;

            int j = 0;

            while (j<5)

            {

                memset(msg,'',sizeof(msg));

                ssize_t s = read(fd[0], msg, sizeof(msg));

                if (s>0)

                {

                    msg[s-1] = '';

                }

                printf("%s %d ",msg,j);

                j++;

            }

            close(fd[0]);

            pid_t ret = waitpid(id, &status, 0);

            printf("exit single(%d),exit(%d) ",status & 0xff, (status >> 8) & 0xff);

        }

        else

        {

            printf("fork error ");

            return 2;

        }

        return  0;

    }

    在这段代码中,子进程关闭读端,每隔2秒向管道写数据,父进程关闭写端,当读了5次数据后就关闭读端。此时再往管道写数据则会报错。

    如果一个管道的写端一直在写,而读端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只写不读再次调用write会导致管道堵塞;

    如果一个管道的读端一直在读,而写端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只读不写再次调用read会导致管道堵塞;

    而当他们的引用计数等于0时,只写不读会导致写端的进程收到一个SIGPIPE信号,导致进程终止,只写不读会导致read返回0,就像读到⽂件末尾⼀样。

  • 相关阅读:
    matlab之simulink仿真入门
    20160205.CCPP体系具体解释(0015天)
    logistic回归具体解释(二):损失函数(cost function)具体解释
    Java 垃圾回收之垃圾回收算法
    synchronized
    如何中断线程
    yield函数
    Linux
    notify和notifyAll的区别
    Sleep和Wait的区别
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/9785543.html
Copyright © 2020-2023  润新知