バッファリングされた 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)関数の正しい実装は次のとおりです:
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 実装では、ストリームが最初に使用されるときにこのテストが行われます。
printf()
自動的にフラッシュされます。fflush()
を除いてフラッシュされません。 、大量のデータを書き込む場合を除きます。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 のこの行を参照してください