web-dev-qa-db-ja.com

stdoutをメモリにバッファリングし、専用スレッドから書き込む方法

多くのワーカースレッドを持つCアプリケーションがあります。これらがブロックされないことが重要であるため、ワーカースレッドがディスク上のファイルに書き込む必要がある場合は、メモリ内の循環バッファーに書き込み、そのバッファーをディスクに書き込むための専用スレッドを用意します。

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

これはすべてうまく機能します。私の質問は、stdoutに似たようなものを実装するにはどうすればよいですか?

マクロprintf()を使用してメモリバッファに書き込むことはできますが、stdoutに書き込む可能性のあるすべてのコードを制御することはできません(一部はサードパーティライブラリにあります)。

考え? NickB

23
NickB

freopenを使用するというアイデアが好きです。 dup および dup2 を使用して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;
}
34
Nate Kohl

GNU libcを使用している場合は、 メモリストリーム を使用できます。

9
diapir

freopen()を使用して、stdoutをファイルに「リダイレクト」できます。

man freopen言う:

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

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

4
qrdl

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

~MSalters/# YourCurrentApp | bufcat
2
MSalters

4096 bigbufを使用する方法は、一種の作業のみを行います。私はこのコードを試しましたが、stdoutをバッファに正常にキャプチャできますが、実際のケースでは使用できません。キャプチャされた出力の長さを知る方法がないため、文字列 '\ 0'をいつ終了するかを知る方法はありません。バッファを使用しようとすると、96文字のstdout出力を正常にキャプチャした場合、4000文字のガベージが吐き出されます。

私のアプリケーションでは、CプログラムでPerlインタープリターを使用しています。 Cプログラムでスローされたドキュメントからどれだけの出力が吐き出されるのかわかりません。したがって、上記のコードでは、その出力をどこにでもきれいに印刷することはできません。

0
Bob

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

[編集]

stdoutは実際にはFILE*です。既存のコードがFILE*sで機能する場合、それがstdoutで機能するのを妨げる原因がわかりません。

0

1つの解決策(両方のことに対して)は、 writev を介して収集書き込みを使用することです。

たとえば、各スレッドはsprintfをiovecバッファーに入れてから、iovecポインターをライタースレッドに渡し、stdoutを使用してwritevを呼び出すだけで済みます。

これは Advanced Unix Programming からwritevを使用する例です。

Windowsでは、同様の機能にWSAsendを使用します。

0