標準出力バッファリング

例えば、1秒ごとに「.」(ドット)を表示するプログレスバー的なものを考えてみます。

  1 /* progress.c */
  2 #include <stdio.h>
  3 #include <unistd.h>
  4
  5 int main(int argc, char *argv[])
  6 {
  7     int i;
  8
  9     for(i=0; i<10; i++){
 10         printf(".");
 11         sleep(1);
 12     }
 13
 14     return 0;
 15 }

これをコンパイルして実行すると、、、

$ ./progress
(この間、10秒経過)
..........$

プログラム起動から10秒経過後、終了と同時にプログレスバーがドンと表示されました。

このprintf(3)ですが、引数で渡された文字列を、呼び出されたタイミングでリアルタイムに出力しているわけではなく、いったんライブラリ内部のストリームバッファに溜め込んでおき、あるタイミングが来た時に、一気にバッファの内容を書き出します。あるタイミングとは、

  1. 改行コードを含む文字列がバッファに書き込まれた
  2. バッファが一杯になった
  3. プログラムが終了した

上記イベントのうち、いずれかが最初に発生したタイミングで出力されます。なので、改行コードが書き込まれる前にバッファが一杯になった場合、その時点で出力されます。

改行コードを含む文字列がバッファに書き込まれた場合ですが、例えば「printf(”aaa\nbbb”)」を呼出した場合、「”aaa\n”」が出力されます。なので正確には「バッファの先頭から最初の改行コードまでを出力する」です。

リアルタイムで出力するには、printf(3)を呼び出す以前にバッファリングを切っておくか、

setvbuf(STDOUT, NULL, _IONBUF, 0);

printf(3)の直後にフラッシュするか、

fflush(STDOUT);

あるいはstdbuf(1)コマンドで以下のように実行することで、printf(3)呼出しのタイミングでバッファの内容が吐き出されます。

$ stdbuf -o0 <command>