35

の動作はprintf()の場所に依存するようですstdout

  1. stdoutコンソールに送信されると、printf()行バッファリングされ、改行が出力された後にフラッシュされます。
  2. がファイルstdoutにリダイレクトされた場合、fflush()が呼び出されない限り、バッファはフラッシュされません。
  3. さらに、がファイルにリダイレクトprintf()される前に使用された場合stdout、(ファイルへの) 以降の書き込みは行バッファリングされ、改行の後にフラッシュされます。

いつstdout行バッファリングされ、いつfflush()呼び出される必要がありますか?

それぞれの最小限の例:

void RedirectStdout2File(const char* log_path) {
    int fd = open(log_path, O_RDWR|O_APPEND|O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO);
    dup2(fd,STDOUT_FILENO);
    if (fd != STDOUT_FILENO) close(fd);
}

int main_1(int argc, char* argv[]) {
    /* Case 1: stdout is line-buffered when run from console */
    printf("No redirect; printed immediately\n");
    sleep(10);
}

int main_2a(int argc, char* argv[]) {
    /* Case 2a: stdout is not line-buffered when redirected to file */
    RedirectStdout2File(argv[0]);
    printf("Will not go to file!\n");
    RedirectStdout2File("/dev/null");
}
int main_2b(int argc, char* argv[]) {
    /* Case 2b: flushing stdout does send output to file */
    RedirectStdout2File(argv[0]);
    printf("Will go to file if flushed\n");
    fflush(stdout);
    RedirectStdout2File("/dev/null");
}

int main_3(int argc, char* argv[]) {
    /* Case 3: printf before redirect; printf is line-buffered after */
    printf("Before redirect\n");
    RedirectStdout2File(argv[0]);
    printf("Does go to file!\n");
    RedirectStdout2File("/dev/null");
}
4

3 に答える 3

41

のフラッシングstdoutは、そのバッファリング動作によって決定されます。バッファリングは、(フルバッファリング:可能であれば_IOFBF待機する)、 (ラインバッファリング:改行が自動フラッシュをトリガーする)、および(常に直接書き込みが使用される)の3つのモードに設定できます。「これらの特性のサポートは実装によって定義されており、および関数を介して影響を受ける可能性があります。」[C99:7.19.3.3]fflush()_IOLBF_IONBFsetbuf()setvbuf()

「プログラムの起動時に、3つのテキストストリームが事前定義されており、明示的に開く必要はありません。標準入力(従来の入力の読み取り用)、標準出力(従来の出力の書き込み用)、および標準エラー(診断出力の書き込み用)です。最初に開いたとき、標準エラーストリームは完全にバッファリングされていません。標準入力ストリームと標準出力ストリームは、ストリームがインタラクティブデバイスを参照していないと判断できる場合にのみ完全にバッファリングされます。」[C99:7.19.3.7]

観察された行動の説明

したがって、何が起こるかというと、実装はプラットフォーム固有の何かを実行して、stdoutラインバッファリングされるかどうかを決定します。ほとんどのlibc実装では、このテストはストリームが最初に使用されるときに実行されます。

  1. 動作#1は簡単に説明できます。ストリームがインタラクティブデバイス用の場合、ラインバッファリングされ、printf()自動的にフラッシュされます。
  2. ケース#2も予想されます。ファイルにリダイレクトすると、ストリームは完全にバッファリングされ、fflush()大量のデータを書き込まない限り、を除いてフラッシュされません。
  3. 最後に、基礎となるfdのチェックを1回だけ実行する実装についても、ケース3を理解しています。最初にstdoutのバッファを強制的に初期化したためprintf()、stdoutはラインバッファモードを取得しました。fdをスワップアウトしてファイルに移動しても、ファイルはまだ行バッファリングされているため、データは自動的にフラッシュされます。

いくつかの実際の実装

C99は「対話型デバイス」が何であるかを指定しておらず、POSIXのstdioエントリもこれを拡張していないため(stderrを読み取り用に開く必要があることを超えて)、各libcはこれらの要件を解釈する方法に自由度があります。

  1. Glibc。filedoalloc.c:L111を参照してください。ここではstat()、fdがttyであるかどうかをテストし、それに応じてバッファリングモードを設定するために使用します。(これはfileops.cから呼び出されます)stdout最初はnullバッファーがあり、fd1の特性に基づいてストリームの最初の使用時に割り当てられます。

  2. BSDlibc。非常に似ていますが、従うべきはるかにクリーンなコードです!makebuf.cのこの行を参照してください

于 2012-12-18T13:07:12.393 に答える
0

ファイル記述子を閉じないでください。メッセージをファイルにのみ出力する場合は、削除close(fd)して閉じてください。 stdout_bak_fd

于 2012-12-18T12:43:31.717 に答える