如何在内存中缓冲stdout并从专用线程写入
我有一个包含许多工作线程的C应用程序。 至关重要的是这些不会阻塞工作线程需要写入磁盘上的文件,我让它们写入内存中的循环缓冲区,然后有一个专用线程将该缓冲区写入磁盘。
工作线程不再阻塞。 专用线程可以在写入磁盘时安全地阻塞,而不会影响工作线程(写入磁盘时它不会保持锁定)。 我的内存缓冲区调整得足够大,以至于编写器线程可以跟上。
一切都很好。 我的问题是,我如何为stdout实现类似的东西?
我可以将macro printf()写入内存缓冲区,但是我无法控制可能写入stdout的所有代码(其中一些代码在第三方库中)。
思考? NickB
我喜欢使用freopen
的想法。 您也可以使用dup和dup2将stdout
重定向到管道,然后使用read
从管道中获取数据。
像这样的东西:
#include #include #include #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; }
如果您正在使用GNU libc,则可以使用内存流 。
您可以使用freopen()
将stdout
“重定向”到文件中。
man freopen
说:
freopen()函数打开文件,该文件的名称是path指向的字符串,并将stream指向的流与它相关联。 原始流(如果存在)已关闭。 mode参数与fopen()函数一样使用。 freopen()函数的主要用途是更改与标准文本流(stderr,stdin或stdout)关联的文件。
这个文件可能是一个管道 – 工作线程将写入该管道,编写器线程将侦听。
为什么不将整个应用程序包装在另一个中? 基本上,你想要的是一个聪明的cat
,它将stdin复制到stdout,必要时进行缓冲。 然后使用标准的stdin / stdout重定向。 这可以在不修改当前应用程序的情况下完成。
~MSalters/# YourCurrentApp | bufcat
您可以使用setvbuf()
或setbuf()
更改缓冲的工作方式。 这里有一个描述: http : //publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html 。
[编辑]
stdout
确实是一个FILE*
。 如果现有代码与FILE*
一起使用,我看不出阻止它使用stdout
。
一个解决方案(对于你正在做的事情)将是通过writev使用聚集写入。
每个线程可以例如sprintf到一个iovec缓冲区然后将iovec指针传递给writer线程并让它简单地用stdout调用writev。
以下是使用Advanced Unix Programming中的 writev的示例
在Windows下,您将使用WSAsend来实现类似function。
使用4096 bigbuf的方法只会起作用。 我已经尝试过这段代码,虽然它成功地将stdout捕获到缓冲区中,但它在现实世界中无法使用。 您无法知道捕获的输出有多长,因此无法知道何时终止字符串’\ 0’。 如果您尝试使用缓冲区,如果您已成功捕获96个字符的stdout输出,则会获得4000个字符的垃圾吐出。
在我的应用程序中,我在C程序中使用perl解释器。 我不知道在C程序中抛出什么文件会产生多少输出,因此上面的代码永远不会允许我在任何地方干净地打印输出。