📜  pipe()系统调用

📅  最后修改于: 2021-05-26 02:49:23             🧑  作者: Mango

先决条件: I / O系统调用

从概念上讲,管道是两个流程之间的连接,以使一个流程的标准输出成为另一流程的标准输入。在UNIX操作系统中,管道对于相关进程之间的通信(进程间通信)很有用。

  • 管道仅是单向通信,即我们可以使用管道,以便一个进程向管道写入数据,而另一个进程从管道读取数据。它打开一个管道,该管道是被视为“虚拟文件”的主内存区域。
  • 创建过程及其所有子过程均可使用该管道进行读取和写入。一个进程可以写入此“虚拟文件”或管道,而另一个相关进程可以从中读取。
  • 如果某个进程在将某些内容写入管道之前尝试读取,则该过程将被挂起,直到写入某些内容为止。
  • 管道系统调用在进程的打开文件表中找到前两个可用位置,并将其分配给管道的读取和写入端。


    C语言语法:

    int pipe(int fds[2]);
    
    Parameters :
    fd[0] will be the fd(file descriptor) for the 
    read end of pipe.
    fd[1] will be the fd for the write end of pipe.
    Returns : 0 on Success.
    -1 on error.

    管道的行为为FIFO (先进先出),管道的行为类似于队列数据结构。读写大小在这里不必匹配。我们一次只能写入512个字节,但在管道中一次只能读取1个字节。

    // C program to illustrate
    // pipe system call in C
    #include 
    #include 
    #define MSGSIZE 16
    char* msg1 = "hello, world #1";
    char* msg2 = "hello, world #2";
    char* msg3 = "hello, world #3";
      
    int main()
    {
        char inbuf[MSGSIZE];
        int p[2], i;
      
        if (pipe(p) < 0)
            exit(1);
      
        /* continued */
        /* write pipe */
      
        write(p[1], msg1, MSGSIZE);
        write(p[1], msg2, MSGSIZE);
        write(p[1], msg3, MSGSIZE);
      
        for (i = 0; i < 3; i++) {
            /* read pipe */
            read(p[0], inbuf, MSGSIZE);
            printf("% s\n", inbuf);
        }
        return 0;
    }
    

    输出:

    hello, world #1
    hello, world #2
    hello, world #3
    

    父母和孩子共享管道

    当我们在任何进程中使用fork时,文件描述符在子进程和父进程之间保持打开状态。如果我们在创建管道后调用fork,则父级和子级可以通过管道进行通信。

    以下程序的输出。

    // C program to illustrate
    // pipe system call in C
    // shared by Parent and Child
    #include 
    #include 
    #define MSGSIZE 16
    char* msg1 = "hello, world #1";
    char* msg2 = "hello, world #2";
    char* msg3 = "hello, world #3";
      
    int main()
    {
        char inbuf[MSGSIZE];
        int p[2], pid, nbytes;
      
        if (pipe(p) < 0)
            exit(1);
      
        /* continued */
        if ((pid = fork()) > 0) {
            write(p[1], msg1, MSGSIZE);
            write(p[1], msg2, MSGSIZE);
            write(p[1], msg3, MSGSIZE);
      
            // Adding this line will
            // not hang the program
            // close(p[1]);
            wait(NULL);
        }
      
        else {
            // Adding this line will
            // not hang the program
            // close(p[1]);
            while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0)
                printf("% s\n", inbuf);
            if (nbytes != 0)
                exit(2);
            printf("Finished reading\n");
        }
        return 0;
    }
    

    输出:

    hello world, #1
    hello world, #2
    hello world, #3
    (hangs)         //program does not terminate but hangs
    

    在此代码中,在完成读/写之后,父级和子级都阻塞而不是终止进程,这就是程序挂起的原因。发生这种情况是因为读取系统调用获取的数据量与管道请求的数据量或管道的数据量(以较少者为准)相同。

    • 如果管道为空,并且我们调用读取系统调用,那么如果没有进程打开写入端,则管道上的读取将返回EOF(返回值0)。
    • 如果某个其他进程打开了用于写入的管道,则读取将阻止新的数据,因此此代码输出将挂起,因为此处写入结束了父进程,并且子进程也没有关闭。
    想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。