如何在内存中缓冲stdout并从专用线程写入

时间:2009-06-05 13:52:15

标签: c file stdout buffering

我有一个包含许多工作线程的C应用程序。至关重要的是这些不会阻塞工作线程需要写入磁盘上的文件,我让它们写入内存中的循环缓冲区,然后有一个专用线程将该缓冲区写入磁盘。

工作线程不再阻塞。专用线程可以在写入磁盘时安全地阻塞,而不会影响工作线程(写入磁盘时它不会保持锁定)。我的内存缓冲区调整得足够大,以至于编写器线程可以跟上。

这一切都很棒。我的问题是,我如何为stdout实现类似的东西?

我可以将macro printf()写入内存缓冲区,但是我无法控制可能写入stdout的所有代码(其中一些代码在第三方库中)。

思考? NickB

7 个答案:

答案 0 :(得分:33)

我喜欢使用freopen的想法。您也可以使用dupdup2stdout重定向到管道,然后使用read从管道中获取数据。

像这样:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX_LEN 40

int main( int argc, char *argv[] ) {
  char buffer[MAX_LEN+1] = {0};
  int out_pipe[2];
  int saved_stdout;

  saved_stdout = dup(STDOUT_FILENO);  /* save stdout for display later */

  if( pipe(out_pipe) != 0 ) {          /* make a pipe */
    exit(1);
  }

  dup2(out_pipe[1], STDOUT_FILENO);   /* redirect stdout to the pipe */
  close(out_pipe[1]);

  /* anything sent to printf should now go down the pipe */
  printf("ceci n'est pas une pipe");
  fflush(stdout);

  read(out_pipe[0], buffer, MAX_LEN); /* read from pipe into buffer */

  dup2(saved_stdout, STDOUT_FILENO);  /* reconnect stdout for testing */
  printf("read: %s\n", buffer);

  return 0;
}

答案 1 :(得分:8)

如果您正在使用GNU libc,则可以使用memory streams

答案 2 :(得分:4)

您可以使用stdoutfreopen()“重定向”到文件中。

man freopen说:

  

freopen()函数打开文件   其名称是指向的字符串   通过路径并关联流   用它指向溪流。该   原始流(如果存在)是   关闭。使用mode参数   就像在fopen()函数中一样。   freopen()的主要用途   功能是更改文件   与标准文本相关联   stream(stderr,stdin或stdout)。

这个文件可能是一个管道 - 工作线程将写入该管道,编写器线程将监听。

答案 3 :(得分:2)

为什么不将整个应用程序包装在另一个应用程序中?基本上,你想要的是一个聪明的cat,它将stdin复制到stdout,必要时进行缓冲。然后使用标准的stdin / stdout重定向。这可以在不修改当前应用程序的情况下完成。

~MSalters/# YourCurrentApp | bufcat

答案 4 :(得分:0)

您可以使用setvbuf()setbuf()更改缓冲的工作方式。这里有一个描述:http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html

[编辑]

stdout确实是FILE*。如果现有代码与FILE*一起使用,我看不出有什么阻止它使用stdout

答案 5 :(得分:0)

一个解决方案(对于你正在做的事情)将是通过writev使用聚会写作。

每个线程可以例如sprintf到一个iovec缓冲区,然后将iovec指针传递给writer线程,让它只需用stdout调用writev。

以下是使用Advanced Unix Programming

中的writev的示例

在Windows下,您可以使用WSAsend来实现类似的功能。

答案 6 :(得分:0)

使用4096 bigbuf的方法只会起作用。我已经尝试过这段代码了,虽然它确实成功地将stdout捕获到缓冲区中,但它在现实世界中无法使用。您无法知道捕获的输出有多长,因此无法知道何时终止字符串'\ 0'。如果您尝试使用缓冲区,如果您已成功捕获96个字符的stdout输出,则会获得4000个字符的垃圾吐出。

在我的应用程序中,我在C程序中使用perl解释器。我不知道在C程序中抛出什么文件会产生多少输出,因此上面的代码永远不会允许我在任何地方干净地打印输出。