UNIX/Linux IPC:从管道读取。如何知道运行时数据的长度?

UNIX/Linux IPC:从管道读取。如何知道运行时数据的长度?,linux,unix,ipc,pipe,Linux,Unix,Ipc,Pipe,我有一个子进程,它生成一些可变长度的输出,然后使用半双工管道将其发送给父进程。在父级中,如何使用read()函数? 由于每次数据的长度可能不同,我如何在运行时知道数据的大小,以便对缓冲区执行任何malloc()?可以在管道文件描述符上使用fstat()函数吗 我知道read()函数将读取指定数量的字节,但如果在读取请求的字节之前到达文件末尾(不是EOF字符),则返回0 我专门使用2.6.27-9内核运行Ubuntu GNU/Linux Richard Stevens在UNIX环境中的高级编程中的

我有一个子进程,它生成一些可变长度的输出,然后使用半双工管道将其发送给父进程。在父级中,如何使用read()函数? 由于每次数据的长度可能不同,我如何在运行时知道数据的大小,以便对缓冲区执行任何malloc()?可以在管道文件描述符上使用fstat()函数吗

我知道read()函数将读取指定数量的字节,但如果在读取请求的字节之前到达文件末尾(不是EOF字符),则返回0

我专门使用2.6.27-9内核运行Ubuntu GNU/Linux

Richard Stevens在UNIX环境中的高级编程中的所有示例都指定了写入管道时的数据长度,或者依赖于fgets()stdio.h函数。因为我关心速度,所以我想尽量避免使用stdio.h

使用共享内存是否一定会更快

谢谢,
-Dhruv

为什么不将长度作为(比如)第一个“n”字节写入管道?然后在另一端,您可以读取这些字节,确定长度,然后读取字节数(即,您有一个非常简单的协议)

由于写入端总是可以向管道写入更多数据,因此无法知道其中数据的大小。您可以让发送方先写入长度,也可以分配一个较大的缓冲区,尽可能多地读取,如果缓冲区不够大,则调整其大小


共享内存会更快,因为它可以避免复制,也可以避免一些系统调用,但是跨shmem传输数据所需的锁定协议更复杂,并且容易出错,因此通常最好避免共享内存,除非您绝对需要它。此外,对于共享内存,在分配缓冲区时,必须为要传输的数据设置固定的最大大小。

由于没有大小,因此无法从管道中获取任何大小信息

您需要使用定义的大小或分隔符

换句话说,在子级中,将即将输出的大小输出为int,然后写入实际输出;在父级中,您读取大小(它是一个int,因此大小始终相同),然后读取那么多字节


或者:定义一个结束字符,直到你看到为止,假设你需要继续阅读。然而,这可能需要某种转义/编码机制,而且可能不会那么快。我认为这基本上就是fgets所做的。

其他海报是正确的:你必须有自己指定数据包长度的方法。一种具体、实用的方法是使用。它的创建和解析非常简单,并且受到一些常见框架的支持,例如。

由于您似乎打算从管道中一次性读取所有数据,因此我认为以下技术将比其他答案中建议的delimiter+编码或miniheader技术更好地为您服务:

从管道(7)手册页:

如果所有文件描述符引用 管道的写入端已被删除 关闭,然后尝试读取(2) 从管道将看到文件的结尾 (读取(2)将返回0)

下面的示例取自管道(2)手册页,并颠倒过来,以便孩子进行书写,家长进行阅读(只是为了确保)。我还添加了一个可变大小的缓冲区。孩子会睡5秒钟。延迟将确保子项的exit()与pipeio无关(父项将在子项退出之前打印完整的行)

#包括
#包括
#包括
#包括
#包括
#包括
煤焦*
slurpfd(int-fd)
{
const int bytes_在_a_时间=2;
char*read_buffer=NULL;
int buffer_size=0;
int buffer_offset=0;
int chars_io;
而(1){
if(缓冲区偏移量+时间上的字节数>缓冲区大小){
buffer\u size=每次字节数+buffer\u size*2;
read\u buffer=realloc(read\u buffer,buffer\u size);
如果(!读取缓冲区){
佩罗(“记忆”);
退出(退出失败);
}
}
字符io=读取(fd,
读取缓冲区+缓冲区偏移量,
字节(每次为字节);

如果(chars_io如果您的消息不太大,您可以尝试使用IPC消息队列。

感谢大家的回复。通过管道发送数据的子进程基本上是一个工具的执行输出,该工具列出了一些有关系统的统计信息。每次输出的长度都不同。我已将标准输出复制到wri子进程中管道的末端。我的理解是,执行工具后的子进程将自动将输出放在管道的写入端,因为我复制了标准输出。在父进程中完成等待后,我应该能够使用read()从管道读取数据。如何获取read()的长度在parent?中,我将对收集的输出进行一些解析。由于该工具的输出使用换行符和空格进行了很好的格式化,因此我可以使用指针遍历缓冲区,而不是将中间数据存储在磁盘上的文件中,然后使用诸如sscanf()之类的string.h函数,这样做更容易、更有效。我想知道在管道上连续执行lseek()是否有助于获取数据的大小。请阅读我的答案的更新。如果您想在数据可用时读取数据,它将告诉您该怎么做。您不能使用lseek()在管道上。尽管它有一个文件描述符,并且有点像文件,但管道和套接字一样都不是文件。在等待子进程完成数据写入后,我使用mmap()打开转储的数据文件,并使用fstat()查找文件大小。解析很容易,因为我知道转储文件的格式。谢谢;您的示例代码几乎对我有用,只是它无法null终止读取缓冲区!是的,这是真的。调用puts(slurpfd()。
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *
slurpfd(int fd)
{
    const int bytes_at_a_time = 2;
    char *read_buffer = NULL;
    int buffer_size = 0;
    int buffer_offset = 0;
    int chars_io;
    while (1) {
      if (buffer_offset + bytes_at_a_time > buffer_size) {
        buffer_size = bytes_at_a_time + buffer_size * 2;
        read_buffer = realloc(read_buffer, buffer_size);
        if (!read_buffer) {
          perror("memory");
          exit(EXIT_FAILURE);
        }
      }

      chars_io = read(fd,
                  read_buffer + buffer_offset,
                  bytes_at_a_time);
      if (chars_io <= 0) break;
      buffer_offset += chars_io;
    }

    if (chars_io < 0) {
      perror("read");
      exit(EXIT_FAILURE);
    }

    return read_buffer; /* caller gets to free it */
}

int
main(int argc, char *argv[])
{
  int pipefd[2];
  pid_t cpid;

  assert(argc == 2);

  if (pipe(pipefd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  cpid = fork();
  if (cpid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (cpid == 0) {     /* Child writes argv[1] to pipe */
    close(pipefd[0]);  /* Close unused read end */

    write(pipefd[1], argv[1], strlen(argv[1]) + 1);

    close(pipefd[1]);  /* Reader will see EOF */
    /* sleep before exit to make sure that there
       will be a delay after the parent prints it's
       output */
    sleep(5);
    exit(EXIT_SUCCESS);
  } else {             /* Parent reads from pipe */
    close(pipefd[1]);  /* Close unused write end */

    puts(slurpfd(pipefd[0]));

    close(pipefd[0]);
    wait(NULL);        /* Wait for child */
    _exit(EXIT_SUCCESS);
  }
}