通过管道读取和写入数据

Posted

技术标签:

【中文标题】通过管道读取和写入数据【英文标题】:Reading and writing data through a pipe 【发布时间】:2011-09-07 13:08:10 【问题描述】:

我使用 fork() 创建了两个进程。子进程正在生成并将可变数量的数据(数组字符)连续写入管道。父进程从管道中读取数据并将接收到的数据打印到标准输出。

代码很简单:

switch (fork()) 
  case -1: 
    exit (1);
    break;
  case 0:
    close(fd[0]);
    generate_data(fd[1]);
    break;
  default:
    close(fd[1]);
    while(1) 
        n = read(fd[0], readbuffer, sizeof(readbuffer));
        readbuffer[n] = 0;
        if (n > 0)
            printf ("read: %s\n", readbuffer);            
        else
            exit(1);
       
   break;
   

generate_data(int) 遍历一个列表,将每个元素(字符串)写入作为参数给出的文件描述符(在这种情况下是管道的写入端):

void generate_data(int fd) 

   node_t node* = list;
   while (node != NULL) 
     write(fd, node->data, strlen(node->data)+1);
     node = node->next();
   


这里的问题是输出总是不可预测的:当另一个进程正在处理最后一个read时,子进程将数据写入管道,所以当它再次调用读取时,其余数据不再存在.

根据man 2 pipe,这不应该发生:

写入管道写入端的数据由 内核,直到它从管道的读取端读取。

获取 10 个元素的列表,一些输出示例:

示例 1:

read: element_4
read: element_8
read: element_9

示例 2:

read: element_7
read: element_8
read: element_9
read: element_10

示例 3:

read: element_2
read: element_8

有人知道这里发生了什么吗?

【问题讨论】:

不是没有显示写作的代码,也许还有一些示例输出。 到底是什么问题?次要 nitpick:read() 不读取以 null 结尾的字符串。您的 %s 格式需要一个以 nul 结尾的字符串。也许你应该添加“readbuffer[n] = 0;”阅读后,给予足够的空间。 感谢 cmets。我刚刚用更多信息编辑了这个问题。 仔细阅读我的答案——它准确地解释了问题所在,并且您上面的代码证实了这一点。 generate_data 正在通过管道发送多个以 0 结尾的字符串; printf 只是打印第一个。在generate_data 中用换行符替换零,或者在阅读代码中解析它们。但关键是printf 忽略了你给它的大部分内容,因为它在第一个 0 字节处停止打印。 【参考方案1】:

您调用read 并捕获返回值,但随后您基本上忽略了它;它告诉您readbuffer 中有多少有效字节,但您将readbuffer 视为它包含一个以零结尾的字符串,但这并不一定。事实上,如果您的数据写入过程通过管道发送0 字节,则单个read 可能会给您多个 以零结尾的字符串;使用 printf 意味着您忽略了第二个和后续的。至少,您需要使用fwrite 将特定的正确字节数写入标准输出,尽管我怀疑您实际上需要做的是首先用换行符替换那些零。修改generate_data 以发送换行符而不是零可能是一个更好的主意。

【讨论】:

特别是,可能发生的情况是两个或多个字符串在一个 read() 调用中到达 - 但代码仅打印 first 字符串并忽略其余字符串,所以看起来好像他们已经丢失了。 哦,是的,我打赌就是这样。写进程通过管道发送0字节;他们在数据的中间! @Ernest,你是对的。我刚读完就忘记了readbuffer[n] = 0。对不起。但无论如何,问题仍然存在。 查看我编辑的答案——我最初错过了一个微妙之处。 @Ernest,在玩了一些 gdb 之后,我可以证实你的理论 :-) 非常感谢!这让我发疯了。【参考方案2】:

读取不会在 nul 字符处停止,您可能会在一次 read() 调用中读取两个“消息”。因此,您的读者必须检查第一个 0 之后是否有更多数据(但在读取的 n 个字节内),然后保存。下一次读取调用应将其数据附加到此剩余部分。一种特殊情况是缓冲区中有剩余但尚未完整的消息。

【讨论】:

以上是关于通过管道读取和写入数据的主要内容,如果未能解决你的问题,请参考以下文章

如何多次写入/读取管道

fprintf vs WriteFile 写入管道:无法从所有管道读取

node 文件操作 读取,写入 目录的写入读取 文件流 写入流 管道流 压缩 解压

如何确保已读取所有管道缓冲区?

来自管道的多个固定大小记录的读取器

传递读取的 FD parcelable:何时关闭?