【问题标题】:How to read exactly the message length in pipe?如何准确读取管道中的消息长度?
【发布时间】:2018-10-29 11:06:04
【问题描述】:

我正在制作一个程序,其中孩子从父母编写的管道中读取消息。但问题是:有没有可能孩子在父母完成写入之前就开始在管道中阅读?如果是这样,孩子将阅读消息的片段并终止。如何确保在不逐个字符读取字符并在有字符时循环读取所有消息的情况下读取所有消息?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>

#define TEXT_LENGTH 20

int main(void) {
    int p[2];
    char msg[TEXT_LENGTH];

    if (pipe(p) == -1) {
        perror("Error");
        exit(1);
    }

    switch (fork()) {
        case -1:
            perror("Error");
            exit(1);
        case 0:
            close(p[1]);
            read(p[0], &msg, TEXT_LENGTH);
            printf("%s\n", msg);
            exit(0);
        default:
            close(p[0]);
            strcpy(msg, "Very long text ...\0");
            write(p[1], &msg, TEXT_LENGTH);
            wait(NULL);
    }

    return 0;
}

现在可以了,但是如果文本很长,在孩子进入阅读之前,父母有时间写吗?

【问题讨论】:

  • 如果没有什么要阅读的,阅读器会block。并且:读写都是原子的(至少,最多一个缓冲区长度)
  • 我认为管道在标记为关闭之前不会让您读取它们。
  • 查找 TLV(类型、长度、值)编码。用它。如果父母向孩子发送多条消息(不是给孩子的一条消息,来自孩子的单一响应——你可以更简单地做到这一点),那么 TLV 上的一些变体是必要的,这样你就知道要读取多少数据。在紧要关头,您可以为每条消息使用“结束标记”,但需要小心。
  • 如果您的消息小于管道缓冲区的大小(介于 4 KiB 和 64 KiB 之间),那么您可以原子地编写完整的消息。如果它们比缓冲区长,则必须进行多次读取才能获取所有数据。如果消息很短并且协议是同步的(所以在孩子响应之前的消息之前,父母不会向孩子发送新消息),那么你可以让孩子阅读大块,并且知道它会获取全部信息。您的代码显示了一个写入/读取周期——您的孩子可以简单地读取到 EOF(如果父级关闭管道)。

标签: c unix pipe


【解决方案1】:

从你调用fork的那一刻起,你就可以确定写在管道上的所有东西都会被子进程读取:

  • 如果父级写了一些东西,而子级没有立即读取它,它将留在管道缓冲区中,直到子级读取。
  • 如果孩子在父母开始写作之前开始阅读,那么孩子的read 将阻塞,直到有东西要阅读。

您可能希望在父级中写入close(p[1]),以便子级可以正确识别数据的结尾。在您的特定情况下这不是问题,因为您写入固定数量的数据并读取完全相同的数量,但通常最好在写入后关闭管道。

其实没什么好担心的。如果你有一个非常长的字符串,唯一会更好的事情是在块中读取和写入并在子级中分别处理每个块,这样你的程序就不会使用太多内存来保存它不需要的东西。

另外,附注:当您使用字符串文字 "like this" 时,您不必添加 NUL 终止符 \0,它会自动添加。

【讨论】:

    【解决方案2】:

    有没有可能孩子在父母完成写入之前就开始在管道中阅读?

    这不是问题。 read(2) 调用阻塞直到有东西要读取,除非你将文件描述符标记为非阻塞。

    如何确保在不逐个字符地读取字符并在有字符时循环读取所有消息?

    问题是管道不理解“消息”是什么或其开头或结尾;它只是从一端传递到另一端的字节流。阅读pipe(7)了解详情。

    如果你有任何“开始”、“结束”、“长度”标记,那么你需要自己构造/解构。您可以随时询问 read(2) 更多,如果返回的内容不符合要求,您可以得出结论,这就是全部(同时检查返回值和errno 以了解确切原因:结束-文件、I/O 错误等)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-16
      • 2016-11-05
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多