为什么这个管道工作?

时间:2017-01-31 00:21:59

标签: c unix process pipe

我想了解管道。该程序工作正常(父母发送消息"你好"给它的孩子,孩子打印它。我不明白两件事:

  • 不会在某个时刻暂停程序,因为让我们说孩子关闭了写描述符,同时父程序也会关闭写描述符吗?

    < / LI>
  • 关闭之后为什么不打开描述符?

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(){

    int desc[2];
    pipe(desc);

    int pid = fork();

    if(pid == 0){
        while(1){
            char buffer[100];
            close(desc[1]);
            read(desc[0], buffer, 100);
            printf("Child: recieved message - '%s'\n", buffer);
        }
    }
    if(pid > 0){
        while(1){
            sleep(1);
            char buffer[100];
            strcpy(buffer, "Hello, child!");
            close(desc[0]);
            write(desc[1], buffer, 100);
        }
    }
    return 0;
}

2 个答案:

答案 0 :(得分:2)

也许有关于fork()的一些信息已到位。成功执行后,fork会创建两个almost identical进程。你的情况唯一的区别是fork的返回值(父级接收子级的pid,子级获得0)。因此,管道在两个进程中都打开,文件描述符对两者都有效。这里的关键是这些描述符代表的是复制到两个进程。

因此,当孩子关闭fd时,它正在做的就是销毁自己的文件描述符,而不是它的父文件描述符。

但请注意,fd的关闭应该在循环外发生。虽然不确定(在您的情况下,可能没有发生),但多次关闭同一文件可能会导致程序崩溃。

答案 1 :(得分:1)

这么少的代码,很多小问题:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(){

    int desc[2];
    pipe(desc);

pipe()电话或其他系统电话没有错误检查。我不会进一步评论大多数可能性。

    int pid = fork();

代码实际上处理失败的fork()

    if(pid == 0){
        while(1){
            char buffer[100];
            close(desc[1]);

close()不应位于while循环内。无论如何它在第一次迭代后失败,因此没有造成巨大的破坏,但你不应该反复尝试关闭已经关闭的那个。它应该被移到循环之外。

            read(desc[0], buffer, 100);

您不知道读取了多少数据。您应该注意读取数据的操作结果。如果你不这样做,你很容易处理垃圾。此外,当父母去世时,孩子的返回值为零;没有别的办法可以让孩子死去。您的代码应该测试何时退出循环。

            printf("Child: received message - '%s'\n", buffer);

这假设从管道读取的数据为空终止。这并不总能得到保证,但稍后会看到此计划。

        }
    }
    if(pid > 0){
        while(1){
            sleep(1);
            char buffer[100];
            strcpy(buffer, "Hello, child!");
            close(desc[0]);

另一个错位close();它也应该在循环之外。

            write(desc[1], buffer, 100);

您初始化了您编写的100个字节中的14个字节,包括空字节作为这些字节的最后一个字节。那是幸运的;它可以避免孩子出现未定义的行为。严格来说,父母有不明确的行为;在实践中,这不会是一个问题。可以说,你应该测试write()是否有效。如果孩子死了,父母会得到SIGPIPE并且一切都很好 - 父母也会停止。

        }
    }
    return 0;
}

没有什么可以阻止这些进程无限期地重复。你必须打断它们才能阻止它们。这不是一个长期的好策略。

你问:

  
      
  • 程序是否会在某个时刻暂停,因为让我们说孩子关闭了写描述符,同时父节点也会关闭写描述符?
  •   

父级只能关闭其写入描述符的副本,但除非它退出时不会这样做。子进程可以并且确实在第一次通过循环时关闭其写入描述符的副本(并且在后续循环中尝试执行此操作时失败 - 它将在EBADF中返回errno表示错误与读取描述符类似;父对象在第一个循环上关闭其读取描述符,并在每次后续迭代时失败。子进程在退出之前不会关闭它的读取描述符。进程之间没有干扰。 fork(),在任一进程执行任何更多操作之前,每个方向都有一个常见的打开文件描述,其中包含两个打开的文件描述符(美妙的术语,不是它!)。close()调用会影响文件描述符;当没有文件描述符引用它时,文件描述被删除。

  
      
  • 为什么关闭它们后不需要打开描述符?
  •   

无法重新打开已关闭的管道文件描述符。并且每个进程只关闭它没有使用的管道的末端(这是一个好的做法 - 重要的,甚至,因为如果一个进程尝试从具有写入结尾的管道读取进程将不会获得EOF管道仍然打开。)