2009-06-05 12 views
16

私は多くのワーカースレッドを持つCアプリケーションを持っています。ワーカースレッドがディスク上のファイルに書き込む必要がある場所では、メモリ内の循環バッファに書き込みを行い、そのバッファをディスクに書き込むための専用のスレッドを用意する必要があります。stdoutをメモリにバッファリングして専用スレッドから書き込む方法

ワーカースレッドは、それ以上をブロックしません。専用スレッドはワーカースレッドに影響を与えずにディスクへの書き込み中に安全にブロックすることができます(ディスクへの書き込み中はロックを保持しません)。私のメモリバッファは、ライタスレッドが追いつくことができるように十分に大きく調整されています。

これはすべて素晴らしいです。私の質問は、どのように私はstdoutのために同様のものを実装するのですか?

私はマクロのprintf()メモリバッファに書き込むために、私はstdoutに書き込む可能性のあるすべてのコードを制御できません(それのいくつかは、サードパーティのライブラリである)でした。

思考? NickB

+0

パイプへの書き込みがブロックされる可能性があるため、ここのソリューションはすべて間違っています。 (そして、それらを非ブロックにすると、 'FILE'はフラッシュする必要があるときにブロックすると、回復不能なエラー状態になります。) –

答えて

27

私はfreopenというアイデアが好きです。 dupdup2を使用してstdoutをパイプにリダイレクトしてから、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; 
} 
+0

これは私の問題を解決するように聞こえる。やってみます。ありがとうございました! – NickB

+4

私はこのようなアプローチを試みたとき、パイプに何もないと 'read'ハングを発見しました。しかし、 'long flags = fcntl(out_pipe [0]、F_GETFL);を追加する。フラグ| = O_NONBLOCK; fcntl(out_pipe [0]、F_SETFL、flags); 'initセクションに問題を修正しました。 – aschepler

+0

まさに私が探していたもの! – Marcin

4

freopen()を使用してstdoutをファイルにリダイレクトすることができます。

man freopenは言う:

freopenは()関数は、その名前である文字列がパスで 指さと がそれとstreamが指すストリームを関連付けファイル を開きます。 オリジナルストリーム(存在する場合)は が閉じています。 mode引数は、fopen()関数と同様に で使用されます。 freopenは の主な用途は、() 関数は、標準的なテキスト ストリーム(stderr、stdin、またはstdout)に関連付けられたファイル を変更することです。

このファイルはパイプワーカーになる可能性があります。スレッドはそのパイプに書き込み、書き込みスレッドはリッスンします。

+0

これは私の問題を解決しません。私はprintf()呼び出しを行うスレッドのディスク書き込みを移動しようとしています。 freopen()を使用しても、printf()呼び出しはファイルに書き込まれますが、stdoutとは別のファイルです。ディスクファイルではない "file"をfreopen()に指定することは可能ですか? – NickB

+0

fileではなくpipeを使用します。 – qrdl

0

バッファリングがsetvbuf()またはsetbuf()でどのように機能するか、変更することができます。ここに説明があります:http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html

[編集]

stdoutは本当にFILE*です。既存のコードがFILE*で動作する場合は、stdoutとの連携を妨げるものはありません。

2

アプリケーション全体を別のアプリケーションにラップしてみませんか?基本的には、stdinをstdoutにコピーし、必要に応じてバッファリングするスマートなcatです。標準のstdin/stdoutリダイレクションを使用します。これは、現在のアプリケーションをまったく変更することなく行うことができます。

~MSalters/# YourCurrentApp | bufcat 
8

GNU libcを使用している場合は、memory streamsを使用することができます。 (あなたがやって両方のもののために)

+1

これは本当の正しい答えだと思います。パイプ手法は高価なシステムコールを必要とし、確実にブロックされます。 –

0

一つの解決策は、writev経由で収集書き込みを用いることであろう。

各スレッド

は、例のIOVECバッファにはsprintfとのためのライター・スレッドにIOVECのポインタを渡すと、それは単に標準出力とwritev呼び出す可能性があります。ここで

は、あなたが同様の機能のためのWSASendを使用するWindowsの下でAdvanced Unix Programming

からwritevを使用した例です。

0

作業の4096 bigbufする唯一の並べ替えを使用する方法。私はこのコードを試しましたが、バッファにstdoutを正常に取り込むことはできますが、実際の場合は使用できません。キャプチャされた出力の長さを知る方法がないので、文字列 '\ 0'を終了するタイミングを知る方法がありません。バッファを使用しようとすると、96文字のstdout出力が正常に取得された場合、4000文字のゴミが吐き出されます。

私のアプリケーションでは、Cプログラムでperlインタープリタを使用しています。これまでのCプログラムではどのような出力が吐き出されているか分からないので、上記のコードは決してどこからでもその出力をきれいに印刷することはできません。

+0

bigbufに最初に0を書き込むと、bigbufの最後のバイトがゼロでない限り、文字列がヌル終了することが保証されます。しかしその場合、オーバーフローを検出することができます。 –

関連する問題