【问题标题】:Why does reading from a pipe give garbage values?为什么从管道读取会给出垃圾值?
【发布时间】:2016-03-17 00:48:39
【问题描述】:
// Some code for initialization

int fd[2];
pipe(fd);
int k = fork();

if (k == 0) {    // Child
    dup2(fd[1], fileno(stdout));
    execl("someExecutable", NULL);    // The executable just printfs one line
}
else if (k > 0) {    // Parent
    wait(&status);
    while (read(fd[0], buffer, 1) > 0) {
        printf("%s", buffer);
    }
}

我省略了错误检查。

首先,如果我的可执行文件有printf("some line\n");,我在屏幕上的输出看起来像s?9o?9m?9e?9 ?9l?9i?9n?9e?9。为什么中间会有这些随机字符?

第二,我的阅读永无止境。当可执行文件结束时,管道的读取端应该已经关闭了吗?

谢谢。

【问题讨论】:

  • 您确定buffer 将是一个以空字符结尾的字符串吗?
  • 我们来看管道初始化代码。
  • 这是让我困惑的一件事。它是否为空终止。 o 不是直接在管道缓冲区中的 s 之后出现吗?垃圾值如何插入到两者之间?
  • 我希望,我在这里没有遗漏任何东西。excel() 接受代表新可执行映像的参数,而不是函数调用,例如 excel("/usr/bin/ls", '- al", NULL); 执行 ls 命令以长模式打印所有文件属性。有关详细信息,请参阅linux.die.net/man/2/execve
  • 这是您的 while 循环不退出的原因。 所有管道的写端必须关闭。这意味着父级必须在开始循环之前关闭 fd[1],否则从 fd[0] 读取将无限期阻塞。

标签: c pipe system-calls


【解决方案1】:

您正在打印二进制数据。以下

 while (read(fd[0], buffer, 1) > 0) {
    printf("%s", buffer);
}

将打印直到它得到一个 NULL 即'\0'。试试这个

 while (read(fd[0], buffer, 1) > 0) {
    printf("%.*s", 1, buffer);
}

此代码可能有助于说明有关 printf 和以空字符结尾的字符串的观点...

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

int main(void) {
  srand(time(NULL));
  size_t n = 0;
  //First we create some random data. 
  //Lets assume this is our binary stream
  char *str_cpy = malloc(1024);
  n = 0;
  while(n++ < 1024) {
    str_cpy[n] = rand() % 255;
  } 
  //We have a known string we want to print
  const char *str = "foobar";
  printf("%s\n", str); 
  memcpy(str_cpy, str, 6);//Ooops: forgot to copy the null terminator 
  size_t str_len = strlen(str_cpy);
  // This is unlikely to print 6
  printf("%zu\n", str_len);
  //This is undefined behavior
  printf("%s\n", str_cpy);
  free(str_cpy);
  return 0;
}

【讨论】:

  • 但我不明白为什么垃圾值会放在我的角色之间。我会理解它是否打印了“一些 linegarbagechars”,因为它不是以空值结尾的。
  • @fossdeep - 因为每次调用printf("%s",buffer),函数printf 将一次从缓冲区读取一个字节并写入stdout,直到找到'\0'。这可能是立即发生的,也可能是 1000 字节后发生的,或者它可能会因为尝试读取超出可读内存的末尾而引发分段错误。在您的情况下,看起来您“幸运”到 '\0' 仅超出预期值几个字节。
【解决方案2】:

你必须在printf() 中将一个(指向)以空字符结尾的字符串传递给%s 格式说明符。

要打印一个字符,使用%c 很好。

while (read(fd[0], buffer, 1) > 0) {
    printf("%c", *buffer);
}

【讨论】:

  • 如果缓冲区一次只允许读取/存储 1 个字符,我不明白为什么这与 printf("%s", buffer) 不同。还是一次迭代的缓冲区可能包含更多字符?
【解决方案3】:

如果你的缓冲区被声明为 char 缓冲区,那么在 read() 中传递一个指向它的指针,如果它被声明为 buffer[],那么你可能想要传递大小作为参数而不是 1,就像你一样已经检查了 read() 的状态,如果成功,它应该返回读取的字节数。现在相应地更改 printf("%c"...) 或 printf("%s"...)。

【讨论】:

    猜你喜欢
    • 2016-03-02
    • 2020-12-17
    • 1970-01-01
    • 2022-01-01
    • 2021-10-06
    • 2022-01-14
    • 2023-03-31
    • 1970-01-01
    • 2021-02-20
    相关资源
    最近更新 更多