我有一个subprocess生成一些可变长度的输出,然后使用半双工pipe道将其发送给父进程。 在父,如何使用read()函数? 由于每次数据可以有不同的长度,我怎么能在运行时知道数据的大小来做缓冲区的任何malloc()? fstat()函数可以在pipe道文件描述符上使用吗?
我知道read()函数将读取指定数量的字节,但如果在请求的字节被读取之前到达文件结尾(而不是EOF字符),将返回0。
我专门运行一个2.6.27-9内核的Ubuntu GNU / Linux。
理查德·史蒂文森在UNIX环境下的高级编程中的所有例子都指定了写入pipe道时的数据长度,或者依赖于fgets()stdio.h函数。 由于我关心速度,我想尽可能远离使用stdio.h。
这将有必要共享内存更快?
谢谢,德鲁夫
由于你打算从管道中读取所有的数据,所以我认为下面的内容比其他答案中提到的分隔符+编码或者小写头技术更适合你:
从管道(7)手册页:
如果所有引用管道写入结束的文件描述符都已经关闭,那么尝试从管道读取(2)将会看到文件结束(read(2)将返回0)。
以下示例是从管道(2)手册页中取出的,并且被颠倒过来以便孩子进行写作,父母进行阅读(只是可以肯定)。 我还添加了一个可变大小的缓冲区。 孩子会睡5秒钟。 延迟将确保孩子的退出()与小孩无关(在孩子退出之前,父母将打印完整的一行)。
#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); } }
从你的评论我现在看到,你可能想读取数据,因为它是可用的,更新用户界面或任何,以反映您的系统的状态。 要做到这一点,以非阻塞(O_NONBLOCK)模式打开管道。 重复读取任何可用的内容,直到-1返回并且errno == EAGAIN并执行解析。 重复unil读取返回0,这表明孩子已经关闭了管道。
要为File *函数使用内存缓冲区,可以在GNU C库中使用fmemopen()。
由于写入端总是可以向管道写入更多的数据,因此无法知道数据的大小。 你可以让发送者先写长度,或者你可以分配一个较大的缓冲区,尽可能多地读取缓冲区,如果缓冲区不够大,可以调整缓冲区的大小。
共享内存会更快,因为它避免了副本,并且可能会避免一些系统调用,但是通过shmem传输数据所需的锁定协议更为复杂且容易出错,所以通常最好避免共享内存,除非您绝对需要它。 另外,使用共享内存时,您必须为分配缓冲区时要传输的数据设置固定的最大大小。
由于没有尺寸,您无法从管道获取任何尺寸信息。
您需要使用定义的大小或分隔符。
换句话说,在孩子中,输出即将到来的输出的大小为int,然后写出实际的输出; 在父母你读的大小(这是一个int,所以它总是相同的大小),然后读取很多字节。
或者:定义一个结束字符,直到你看到,假设你需要继续阅读。 这可能需要某种逃避/编码机制,然而,可能不会那么快。 我认为这基本上是fgets所做的。
为什么不把长度写入(比如说)第一个“n”字节呢? 然后在另一端你可以读取这些字节,确定长度,然后读取这个字节数(即你有一个非常简单的协议)
其他海报是正确的:你必须有一种方法来指定你自己的包长度。 一个具体而实际的方法就是使用网络 。 创建和解析起来很简单,并且由一些常见的框架(如Twisted)支持 。
如果消息不太大,可以尝试使用IPC消息队列。