web-dev-qa-db-ja.com

改行がフォーマット文字列に含まれていない限り、printfが呼び出し後にフラッシュしないのはなぜですか?

改行がフォーマット文字列に含まれていないと、呼び出し後にprintfがフラッシュしないのはなぜですか?このPOSIXの動作はありますか?毎回printfがすぐにフラッシュするようにするにはどうすればよいですか?

479
Crazy Chenz

stdoutストリームはバッファリングされているので、改行に達した後(または指示されたとき)にのみバッファ内にあるものを表示します。すぐに印刷する方法がいくつかあります。

代わりにfprintfを使ってstderrに出力します。

fprintf(stderr, "I will be printed immediately");

fflushを使用する必要があるときはいつでも標準出力をフラッシュしてください。

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

編集 :以下のAndy Rossのコメントから、setbufを使って標準出力のバッファリングを無効にすることもできます。

setbuf(stdout, NULL);
627
Rudd Zwolinski

いいえ、それはPOSIXの動作ではなく、ISOの動作です(まあ、それはPOSIXの動作ですが、ISOに準拠している場合のみ)。

標準出力は、対話型デバイスを参照していることが検出できる場合はラインバッファリングされ、それ以外の場合は完全バッファリングされます。そのため、改行を送ってもprintfがフラッシュしない状況があります。例えば:

myprog >myfile.txt

ユーザーと対話しているのであれば、おそらくすべての行を見たいと思うので、これは効率性にとって意味があります。出力をファイルに送信している場合は、相手側にユーザ​​ーがいない可能性が最も高いです(不可能ではありませんが、ファイルの最後尾になる可能性があります)。 - ユーザーがすべての文字を見たいと主張しているが、それには2つの問題があります。

1つは、あまり効率的ではないということです。 2つ目は、元のANSI Cの使命は、invent new behaviorではなく、主に existing behaviorをコード化することであり、これらの設計決定はANSIがプロセスを開始するずっと前に行われました。現在でもISOでも、規格内の既存の規則を変更するときには非常に慎重に取り組んでいます。

どのように対処するかに関しては、あなたがすぐに見たいと思うあらゆる出力呼び出しの後にfflush (stdout)を行うなら、それは問題を解決するでしょう。

別の方法として、setvbufを操作する前にstdoutを使用してバッファなしに設定することもできます。そうすれば、これらのfflush行すべてをコードに追加することを心配する必要がなくなります。

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

are でファイルに出力を送信すると、パフォーマンスにかなりの影響を及ぼす可能性があることに注意してください。また、これに対するサポートは実装定義であり、規格によって保証されていないことにも注意してください。

ISO C99のセクション7.19.3/3は関連するビットです。

ストリームが バッファなし の場合、文字はできるだけ早くソースからまたは宛先に表示されることを意図しています。そうでなければ、文字は累積され、ブロックとしてホスト環境との間で送受信される可能性があります。

ストリームが 完全にバッファされた の場合、文字は、バッファがいっぱいになったときにブロックとしてホスト環境との間で送受信されることを意図しています。

ストリームが line buffered の場合、文字は改行文字が検出されたときにブロックとしてホスト環境との間で送受信されることを意図しています。

さらに、文字は、バッファーがいっぱいになったとき、バッファリングされていないストリームで入力が要求されたとき、またはホスト環境からの文字の送信を必要とするラインバッファー付きストリームで入力が要求されたときに、ホスト環境にブロックとして送信されます。 。

これらの特性のサポートは実装定義であり、setbufおよびsetvbuf関数を介して影響を受ける可能性があります。

116
paxdiablo

これはおそらく効率のためであり、単一のTTYに複数のプログラムを書き込む場合はこの方法で行の文字がインターレースされないためです。そのため、プログラムAとBが出力している場合、通常は次のようになります。

program A output
program B output
program B output
program A output
program B output

これは臭いが、それはよりも優れています

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

改行でフラッシュすることは保証されていないことに注意してください。したがって、フラッシュが問題になる場合は、明示的にフラッシュする必要があります。

26

すぐにフラッシュするにはfflush(stdout)またはfflush(NULL)を呼び出します(NULLはすべてをフラッシュすることを意味します)。

25
Aaron

注:Microsoftランタイムライブラリはラインバッファリングをサポートしていないので、printf("will print immediatelly to terminal")

http://msdn.Microsoft.com/ja-jp/library/86cebhfs.aspx

15
Renato

stdoutはバッファされているので、改行が表示された後にのみ出力されます。

すぐに出力するには、次のいずれかを行います。

  1. 標準エラー出力.
  2. 標準出力をバッファなしにします。
11
Douglas Leeder

デフォルトでは、stdoutは行バッファ、stderrはバッファなし、fileは完全バッファです。

11
woso

代わりに、バッファなしのstderrにfprintfすることができます。また、必要に応じて標準出力をフラッシュすることもできます。あるいは、stdoutをunbufferedに設定することもできます。

10
Rasmus Kaj

バッファリングを無効にするにはsetbuf(stdout, NULL);を使います。

8

通常、バッファリングには2つのレベルがあります。

1。カーネルバッファーキャッシュ(読み取り/書き込みの高速化)

2。I/Oライブラリのバッファリング(システムコールの数を減らす)

fprintf and write()の例を見てみましょう。

fprintf()を呼び出すと、ファイルに直接書き込まれません。最初にプログラムのメモリのstdioバッファに移動します。そこから、書き込みシステムコールを使用してカーネルバッファキャッシュに書き込まれます。したがって、I/Oバッファーをスキップする1つの方法は、直接write()を使用することです。他の方法は、setbuff(stream,NULL)を使用することです。これにより、バッファリングモードがバッファリングなしに設定され、データがカーネルバッファに直接書き込まれます。データをカーネルバッファーに強制的にシフトするには、「\ n」を使用できます。これは、デフォルトのバッファリングモードが「ラインバッファリング」の場合、I/Oバッファーをフラッシュします。または、fflush(FILE *stream)を使用できます。

これでカーネルバッファーになりました。 Kernel(/ OS)はディスクアクセス時間を最小限に抑えたいため、ディスクのブロックのみを読み書きします。したがって、read()が発行されると、これはシステムコールであり、直接またはfscanf()を介して呼び出すことができ、カーネルはディスクからディスクブロックを読み取り、バッファーに格納します。そのデータは、ここからユーザースペースにコピーされます。

同様に、I/Oバッファーから受信したfprintf()データは、カーネルによってディスクに書き込まれます。これにより、read()write()が高速になります。

カーネルにwrite()を強制的に開始させ、その後、データ転送がハードウェアコントローラーによって制御されるようにするには、いくつかの方法もあります。書き込み呼び出し中にO_SYNCまたは同様のフラグを使用できます。または、fsync(),fdatasync(),sync()などの他の関数を使用して、カーネルバッファーでデータが利用可能になるとすぐにカーネルが書き込みを開始するようにすることもできます。

1
o_O