理解 pipe() 函数

understanding pipe() function

我正在尝试了解 pipe() 函数的工作原理,我有以下程序示例

int main(void)
{
        int     fd[2], nbytes;
        pid_t   childpid;
        char    string[] = "Hello, world!\n";
        char    readbuffer[80];

        pipe(fd);

        if((childpid = fork()) == -1)
        {
                perror("fork");
                exit(1);
        }

        if(childpid == 0)
        {
                /* Child process closes up input side of pipe */
                close(fd[0]);

                /* Send "string" through the output side of pipe */
                write(fd[1], string, (strlen(string)+1));
                exit(0);
        }
        else
        {
                /* Parent process closes up output side of pipe */
                close(fd[1]);

                /* Read in a string from the pipe */
                nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
                printf("Received string: %s", readbuffer);
        }

        return(0);
}

我的第一个问题是在子进程和父进程中使用 close(fd[0])close(fd[1]) 关闭文件描述符有什么好处。其次,我们在子进程中使用 write 并在父进程中使用 read,但是如果父进程在子进程到达 write 之前达到 read 并尝试从其中没有任何内容的管道中读取怎么办?谢谢!

Daniel Jour 已经以非常简洁易懂的方式为您提供了 99% 的答案:

Closing: Because it's good practice to close what you don't need. For the second question: These are potentially blocking functions. So reading from an empty pipe will just block the reader process until something gets written into the pipe.

我会尽量详细说明。

收盘价:

当一个进程被分叉时,其打开的文件会被复制。

每个进程对其允许打开的文件描述符数量都有限制。如文档中所述:管道的每一侧都是一个 fd,这意味着管道需要两个文件描述符,在您的示例中,每个进程仅使用 one.

通过关闭您不使用的文件描述符,您将释放供应有限的资源,并且您可能会在未来进一步需要这些资源。

例如,如果您正在编写服务器,那么额外的 fd 意味着您可以处理更多的客户端。

此外,尽管在退出时释放资源是 "optional",但这是一个很好的做法。未正确释放的资源应由 OS...

处理

...但是OS也是我们程序员写的,我们确实会出错。因此,只有认领资源并了解资源的人才会好心释放资源才有意义。

竞争条件readwrite 之前):

POSIX 定义了一些行为,使 readwrite 和管道成为线程和进程并发同步的良好选择。您可以在 the Rational section for write 上阅读更多相关信息,但这里有一个简要说明:

默认情况下,管道(和套接字)是在所谓的 "blocking mode" 中创建的。 这意味着应用程序将挂起,直到执行 IO 操作。

另外,IO操作是atomic,意思是:

  • 您永远不会同时阅读和写作。 read 操作将等到 write 操作完成后再从管道读取(反之亦然)

  • 如果两个线程同时调用read,每个线程都会得到一个串行(非并行)响应,从管道顺序读取(或套接字)- 这使管道成为并发处理的好工具。

换句话说,当您的应用程序调用时:

 read(fd[0], readbuffer, sizeof(readbuffer));

您的应用程序将永远等待一些数据可用并等待read操作完成(它将等待80次(sizeof(readbuffer))字节被读取,或者 EOF 状态在读取过程中发生变化)。