23

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

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

这一切都很好。我的问题是,我如何为标准输出实现类似的东西?

我可以宏 printf() 写入内存缓冲区,但我无法控制可能写入标准输出的所有代码(其中一些在第三方库中)。

想法?尼克B

4

7 回答 7

35

我喜欢使用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;
}
于 2009-06-05T14:45:38.120 回答
9

如果您正在使用 GNU libc,您可能会使用内存流 字符串流

于 2009-06-05T14:10:05.603 回答
4

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

man freopen说:

freopen() 函数打开名称为 path 指向的字符串的文件,并将 stream 指向的流与它相关联。原始流(如果存在)已关闭。mode 参数的使用与 fopen() 函数中一样。freopen() 函数的主要用途是更改与标准文本流(stderr、stdin 或 stdout)关联的文件。

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

于 2009-06-05T13:57:06.080 回答
2

为什么不将整个应用程序包装在另一个应用程序中?基本上,您想要的是一个cat将标准输入复制到标准输出并根据需要进行缓冲的智能。然后使用标准的标准输入/标准输出重定向。这可以在完全不修改当前应用程序的情况下完成。

~MSalters/# YourCurrentApp | bufcat
于 2009-06-05T14:08:14.123 回答
0

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

[编辑]

stdout真的是一个FILE*。如果现有代码与FILE*s 一起使用,我看不出是什么阻止了它与stdout.

于 2009-06-05T13:58:02.460 回答
0

一种解决方案(对于您所做的两件事)是通过writev使用收集写入。

例如,每个线程可以 sprintf 进入 iovec 缓冲区,然后将 iovec 指针传递给编写器线程,并让它简单地使用 stdout 调用 writev。

这是使用高级 Unix 编程中的 writev 的示例

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

于 2009-06-05T14:11:32.650 回答
0

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

在我的应用程序中,我在 C 程序中使用了 perl 解释器。我不知道从 C 程序中抛出的任何文档中会吐出多少输出,因此上面的代码永远不会让我在任何地方干净地打印输出。

于 2010-04-26T12:28:51.283 回答