UNIX/Linux IPC:从管道读取.如何在运行时知道数据长度? [英] UNIX/Linux IPC : Reading from a pipe. How to know length of data at runtime?

查看:116
本文介绍了UNIX/Linux IPC:从管道读取.如何在运行时知道数据长度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a child process which generates some output of variable length and then sends it to the parent using a half duplex pipe. In the parent, how do I use the read() function? Since the data can be of different length each time, how can I at run time know the size of the data to do any malloc() for a buffer? Can the fstat() function be used on a pipe file descriptor?

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

I know that the read() function will read a specified number of bytes but will return 0 if the end of file (not the EOF character) is reached before the bytes requested have been read.

我专门在运行带有2.6.27-9内核的Ubuntu GNU/Linux.

I am specifically running Ubuntu GNU/Linux with a 2.6.27-9 Kernel.

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

All examples in Richard Stevens' Advanced Programming in the UNIX Environment have specified either the length of data while writing into the pipe or have relied on fgets() stdio.h function. Since I am concerned with speed, I want to stay away from using stdio.h as much as possible.

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

Will this be necessarily faster with shared memory?

谢谢, -Dhruv

Thanks, -Dhruv

推荐答案

由于您似乎打算从管道中读取所有数据,因此我认为以下内容比delimiter + encoding或miniheader更好.其他答案中建议的技术:

Since it seems that you intend to make a single read of all the data from the pipe, I think the following will serve you better than delimiter+encoding or miniheader techniques suggested in other answers:

从管道(7)联机帮助页中:

From the pipe (7) manpage:

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

If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0).

下面的示例是从管道(2)联机帮助页中获取的,取反了,以便孩子进行写作,父级进行阅读(请确保).我还添加了一个可变大小的缓冲区.孩子将入睡5秒钟.延迟将确保子项的exit()与pipeio无关(父项将在子项退出前打印完整的行).

The following example was taken from the pipe (2) manpage and reversed so that the child does the writing, the parent the reading (just to be sure). I also added a variable size buffer. The child will sleep for 5 seconds. The delay will ensure that the exit() of the child can have nothing to do with pipeio (the parent will print a complete line before the child exits).

#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);
  }
}


从您的评论中,我现在看到您可能想要读取可用的数据,以更新UI或任何其他内容,以反映系统的状态.为此,请以非阻塞(O_NONBLOCK)模式打开管道.重复读取可用的内容,直到返回-1并errno == EAGAIN为止,然后进行解析.重复unil read会返回0,这表示孩子已关闭管道.


From your comment I see now that you may want to read the data as it becomes available, to update the UI or whatever, to reflect your system's status. To do that open the pipe in non-blocking (O_NONBLOCK) mode. Read repeatedly whatever is available until -1 is returnd and errno == EAGAIN and do your parsing. Repeat unil read returns 0, which indicates that the child has closed the pipe.

要将内存缓冲区用于File *函数,可以在GNU C库中使用fmemopen().

To use an in-memory buffer for File* functions you can use fmemopen() in the GNU C library.

这篇关于UNIX/Linux IPC:从管道读取.如何在运行时知道数据长度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆