【问题标题】:reading and writing to a pipe c issue读取和写入管道 c 问题
【发布时间】:2017-08-15 18:45:02
【问题描述】:

这是程序:

  int fd[2];
  char buf1 [31];
  int i;
  char buf2;

  pipe(fd);

  if (fork() == 0) { // child
    close(fd[1]); // close writing pipe
    for(i = 0; i< 20; i++) { 
        read(fd[0], buf1, 30);
        printf("%s\n", buf1);

    }
    close(fd[0]);
  }

  else { // parent
    close(fd[0]); // close reading end
    buf2 = 'a';
    for (i = 0; i < 20; i++) {
        write(fd[1], &buf2, sizeof(buf2));
    }
    close(fd[1]);
  }
  wait(NULL);

如果里面什么都没有,由于 read 块,我们将首先写入管道。

我的问题是关于 write 功能。 for 循环运行 20 次,每次我都在管道中写入 a。 20次迭代后,写入端的管道将包含aaaaaaaaaaaaaaaaaaaa

在孩子中,我将所有 20 个a's 读入buf1,然后打印结果。

现在管道是空的,因为我们已经写过并阅读了我们写的内容?

由于这里还有一个for循环,所以我们又读了一遍,但是这一次,我认为管道是空的,所以没有什么可读的了。所以我认为阅读20次迭代后的最终输出只是 aaaaaaaaaaaaaaaaaaaa

但实际上是这样的:

aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa

为什么?读完后管道不是空的吗?

【问题讨论】:

  • 读入数据后不清除缓冲区,所以再次打印缓冲区时,第一次读取的数据还在。如果您检查函数调用的返回值,这会更清楚。
  • 还请注意,您在读取数据后未能附加空终止符;因此,依赖有一个是不正确的。你走运了。也可能不是,因为如果你使用了read() 的返回值(正确地)来确定终止符应该去哪里,那么你就不会感到困惑了。
  • @JohnBollinger 这取决于;这不是 MCVE,所以我们不知道buf1 的声明在哪里以及如何发生。如果它是静态的而不是在main 中发生,或者如果它有一个未包含在此处的初始化,则其余字符为 0 并且是安全的。如果所有这些都在main 中,那么是的,这可能是个问题。
  • @DanielH,buf1 的声明在提供的代码中。第二行。
  • @JohnBollinger 但它是在main 中,还是在文件范围内?根据缩进,可能是main,但我们不知道。

标签: c pipe fork


【解决方案1】:

你是对的,如果孩子在父母写任何东西之前调用read,调用将阻塞;但是,在父级关闭文件描述符后,情况不再如此,read 将为 EOF 返回 0。

read 调用不会清除缓冲区。操作系统成功读取了零个字符,因此更改了缓冲区的前零个字符。也就是说,它根本不会改变buf1。因此,一旦将所有数据读入缓冲区,printf 调用将始终执行相同的操作。

请注意,您不能保证获得此输出。孩子很可能会在来自父母的一些但不是全部的write 调用之后运行,在这种情况下,它不会一次读取所有 20 个as 并打印更短的字符串。这就是为什么上面我说printf 调用将总是做同样的事情一旦所有数据都被读取。要处理没有写入所有数据的情况,您应该检查read 的返回值。如果它小于 20,则您没有将完整的字符串读入缓冲区(并且应该再次调用 read 并使用高级目标指针,这样您就不会覆盖您的数据);如果它为零,那么您已经到达文件结尾(写结束已关闭);如果是负数,则表示有错误,您应该检查errno 来处理它。

【讨论】:

  • 那么您是说在第一次读取调用中,读取读取所有 20 个 a,然后,由于管道为空,它不再读取,因此它不会更改缓冲区。所以 20a 被打印了 20 次
  • 是的。虽然这只是运气;你可以得到第一个 read 调用读取 3 as,然后打印,第二个 read 调用读取剩余的 17 as 并打印 17 as(因为它覆盖了前三个),其余的读取调用都在执行 17 as。
  • 好的,谢谢。但我还有一个问题。我明白,当你写一个管道时,首先我们写了一个a,然后我们写了另一个a,现在我们有了aa,当我们阅读时,希望我们能读到aa。我尝试过,而不是a,我决定写hello,然后再写hello。所以我假设在管道上我们有hellohello,但实际上,当我阅读(只有一次阅读调用)时,只打印hello,而不是hellohello。为什么会这样?
  • 原因和我之前的例子一样。如果read 发生在第二个write 之前,那么它只会看到hello。幸运的是,如果您正在写入少量数据(PIPE_BUF 字节,可能是 4096),写入是原子的,您不会得到 hellohe,但如果您有大量写入,则可能。
  • 如何检查返回值。我可以这样做:if (read(fd[0], buf1, 30) != 11) ...我该如何“重读”,因为管道将丢失其数据
猜你喜欢
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多