GNU/Linux >> Linux の 問題 >  >> Linux

ファイルにリダイレクトするときに stdout を明示的にフラッシュする必要があるのはなぜですか?

バッファリングされた IO 関数とバッファリングされていない IO 関数を間違って組み合わせています。このような組み合わせは、特にコードを移植可能にする必要がある場合は、非常に慎重に行う必要があります。 (そして、移植性のないコードを書くのは悪いことです...)
確かに、同じファイル ディスクリプタでバッファ付き IO とバッファなし IO を組み合わせないことが最善です。

バッファリングされた IO: fprintf()fopen()fclose()freopen() ...

バッファリングされていない IO: write()open()close()dup() ...

dup2() を使用する場合 stdout をリダイレクトします。この関数は fprintf() によって満たされたバッファを認識​​していません . dup2() の場合 古い記述子 1 を閉じます。バッファはフラッシュされず、コンテンツは別の出力にフラッシュされる可能性があります。あなたの場合 2a /dev/null に送信されました .

解決策

あなたの場合、 freopen() を使用するのが最善です dup2() の代わりに .これですべての問題が解決します:

<オール>
  • 元の FILE のバッファをフラッシュします ストリーム。 (ケース 2a)
  • 新しく開いたファイルに応じてバッファリング モードを設定します。 (ケース 3)
  • 関数の正しい実装は次のとおりです:

    void RedirectStdout2File(const char* log_path) {
        if(freopen(log_path, "a+", stdout) == NULL) err(EXIT_FAILURE, NULL);
    }
    

    残念ながら、バッファリングされた IO では、新しく作成されたファイルのアクセス許可を直接設定することはできません。他の呼び出しを使用してアクセス許可を変更するか、移植性のない glibc 拡張機能を使用する必要があります。 fopen() man page を参照してください .


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

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

    観察された行動の説明

    つまり、stdout ラインバッファリングされます。ほとんどの libc 実装では、ストリームが最初に使用されるときにこのテストが行​​われます。

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

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

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

  • BSD libc.非常によく似ていますが、よりクリーンなコードに従う必要があります。 makebuf.c のこの行を参照してください


  • Linux
    1. Ssh -tオプションがリダイレクト出力にCrとLfを追加するのはなぜですか?

    2. Unix ファイル システムのファイル モードで「実行」ビットが必要なのはなぜですか?

    3. リダイレクト時にclangが理解できないテキストを生成するのはなぜですか?

    1. ファイルを保存すると、メタデータはどこに移動しますか?

    2. Linux がファイルではなくスワップ パーティションを使用するのはなぜですか?

    3. なぜ rm マニュアルは引数なしで実行できると言っているのに、これは正しくないのですか?

    1. 「vi」エディタで編集すると、iノードの値が変わるのはなぜですか。

    2. ルートユーザーがSudo権限を必要とするのはなぜですか?

    3. RAID 10 デバイスを初期化する必要があるのはなぜですか?