web-dev-qa-db-ja.com

プロセス終了時にバッファは自動的にディスクにフラッシュされますか?

コマンドの出力をファイル(例:echo Hello > file)にリダイレクトすると、そのファイルはコマンド終了直後にそのようなデータを持つことが保証されますか?それとも、コマンド出口とファイルに書き込まれたデータとの間にまだ非常に小さいウィンドウがありますか?コマンドが終了した直後にファイルを読みたいのですが、空のファイルを読みたくありません。

20
Eric

関係するバッファ/キャッシュの多層があります。

  1. CPUキャッシュ.

    データはバイトごとにまとめられ、CPUキャッシュに格納されます。 CPUキャッシュがいっぱいで、データにしばらくアクセスしていない場合、データを含むブロックはメインメモリに書き込まれる可能性があります。これらは、ほとんどの場合、アプリケーションプログラマからは見えません。

  2. インプロセスバッファ.

    データが収集されるプロセスにはある程度のメモリが確保されているので、OSへの要求をできるだけ少なくする必要があります。これは比較的コストがかかるためです。プロセスはこれらのバッファにデータをコピーします。これらのバッファもCPUキャッシュによってバックアップされている可能性があるため、データがメインメモリにコピーされるという保証はありません。アプリケーションは、たとえばfclose(3)またはfsync(3)を使用して、これらのバッファを明示的にフラッシュする必要があります。 exit(3)関数もプロセスが終了する前にこれを行いますが、_exit(2)関数はそうではありません、これが大きな理由です。あなたが何をしているか知っている場合にのみそれを呼び出すためにその関数のマニュアルページの警告。

  3. カーネルバッファ

    その後、OSは自身のキャッシュを保持して、ディスクに送信する必要がある要求の数を最小限にします。このキャッシュは特にプロセスには属していないので、そこにあるデータはすでに終了したプロセスに属している可能性があります。すべてのアクセスがここを通過するため、次のプログラムはここに到達するとデータを参照します。カーネルは、その時間があるとき、または明示的に要求されたときに、このデータをディスクに書き込みます。

  4. ドライブキャッシュ

    ディスクドライブ自体もアクセスを高速化するためにキャッシュを保持します。これらはかなり早く書き込まれ、残りのデータをキャッシュに書き込んでそれが完了したときに報告するコマンドがあります。OSはシャットダウン時にこれを使用して、電源を切る前にデータが書き込まれないようにします。

アプリケーションにとっては、データをカーネルバッファに登録すれば十分です(この時点では実際のデータはCPUキャッシュに残っているかもしれず、メインメモリに書き込まれていないかもしれません)。つまり、プロセス内のバッファはすべてフラッシュされ、データがOSに渡されている必要があります。新しいプロセスを開始すると、要求されたときにOSが同じデータを返すことが保証されます。

21
Simon Richter

アプリケーションに内部キャッシュがない場合は、変更内容がすぐにファイルに書き込まれます。あなたの例でも同じです。ファイルはメモリ内の論理エンティティで、すぐに更新されます。ファイルに対するその後の操作では、プログラムによる変更が表示されます。

ただし、これは変更が物理ディスクに書き込まれたという意味ではありません。変更はOSファイルシステムのキャッシュまたはハードウェアのキャッシュに残る可能性があります。ファイルシステムバッファをフラッシュするには、syncコマンドを使用します。

コマンドが終了した直後にファイルを読みたいのですが、空のファイルを読みたくありません。

ここで実際的な問題に遭遇するべきではありません。

22
mtak

プロセス終了時にバッファは自動的にディスクにフラッシュされますか?

一般に、答えはnoです。

コマンドによって異なります。他の回答が述べているように、ifコマンドは内部的にデータをバッファしません、すべてのデータはコマンドが終了したときに利用可能になります。

しかし、すべてではないにしても、ほとんどの場合、標準I/Oライブラリdo buffer stdoutはデフォルトで(ある程度は)標準であり、アプリケーションが閉じたときのバッファの自動フラッシュに関して異なる保証を与えます。

Cは、通常の出口がバッファー をフラッシュすることを保証します。 「正常終了」とは、明示的に、またはexitから戻ることによって、mainが呼び出されることを意味します。ただし、異常終了によってこの呼び出しが回避される可能性があります(したがって、フラッシュされていないバッファーが残ります)。

これが簡単な例です。

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

これをコンパイルして実行すると、testは必ずnotが標準出力に書き込まれます。

他のプログラミング言語はさらに少ない保証を与えます: Javaは、例えば、プログラム終了時にnot自動フラッシュを行います 。出力バッファに終端されていない行が含まれていると、System.out.flush()が明示的に呼び出されない限り、それは失われる可能性があります。

それでも、あなたの質問本体は少し違った質問をします。データがまったくファイルに入ってきたら、コマンドが終了した直後にそうするべきです(他の答えで説明されている警告に従う)。

21
Konrad Rudolph

私はまだこの問題に十分に対処している質問はないと思います。

コマンドが終了した直後にファイルを読みたいのですが、空のファイルを読みたくありません。

他の答えが説明するように、正常に動作するプログラムはプロセスが正常に終了する前にその内部ファイルバッファをフラッシュします。通常。その後、データが永続ストレージに書き込まれる前に、カーネルまたはハードウェアバッファに残っている可能性があります。 しかし、は、Linuxのファイルシステムセマンティクスは、カーネルが内部的に含むの場合と同じ方法ですべてのプロセスがファイルの内容を見ることを保証します。バッファー1

これは通常、ファイルオブジェクトごとに最大1つのカーネル内バッファを持ち、このバッファを通過するためにすべてのファイルアクセスを要求することによって実装されます。

  • プロセスがファイルを読み込む場合、要求されたファイル部分が現在バッファ内にある場合、カーネルはバッファの内容をプロセスに提示します。もしそうでなければ、カーネルは基礎となる記憶媒体からデータをフェッチし、それをバッファの中に置き、それから前のステップに戻る。

  • プロセスがファイルに書き込むと、データはまずそのファイルのカーネル内バッファの中に置かれます。最終的にバッファの内容はストレージにフラッシュされます。その間に、同じバッファからの読み取りアクセスが満たされます(上記参照)。


1 少なくとも通常のファイル、ディレクトリ、シンボリックリンク用です。 FIFOとソケットは内容が永続的に保存されることは決してないため、別の問題です。通常のファイルには、誰が質問しているかによって内容が変わる特別なケースがいくつかあります。例はprocfsとsysfsの中のファイルです(/proc/selfはシンボリックリンクを読んでいるプロセスのプロセスIDへのシンボリックリンクです)。

9
David Foerster

あなたのコマンドがCランタイムライブラリを使っているプログラムによって実行されると仮定すると、ある時点でそれは開いているファイルを閉じるためにfcloseを呼び出すべきです。

fclose C関数のmanページには、次のように記載されています。

注意事項fclose()は、Cライブラリが提供するユーザ空間バッファのみをフラッシュします。データが物理的にディスクに格納されるようにするには、カーネルバッファもsync(2)やfsync(2)などでフラッシュする必要があります。

fflushのmanページも同じです。 closeのmanページには、次のように記載されています。

カーネルが書き込みを延期するので、正常に終了してもデータがディスクに正常に保存されたことを保証するものではありません。ストリームが閉じられたときにファイルシステムがバッファをフラッシュするのは一般的ではありません。データが物理的に保存されていることを確認する必要がある場合は、fsync(2)を使用してください。 (現時点ではディスクハードウェアに依存します。)

ドライブに同期されていなくても、データは他のプロセスで利用可能です。たぶんそれはあなたにとってすでに十分なものです。

疑問がある場合は、テストを書きます。

5
mvw

コマンドの出力をファイル(例:echo Hello > file)にリダイレクトすると、そのファイルはコマンド終了直後にそのようなデータを持つことが保証されますか?

はいシェルはoutput-fileを開き、echoがそれに直接出力します。コマンドが終了したら、完了です。

それとも、コマンド出口とファイルに書き込まれたデータとの間にまだ非常に小さいウィンドウがありますか?

データがすでにメディア上にあるかどうかは別の問題です。これは、その後ハードウェア障害が発生した場合、またはマウントされたファイルシステムを迂回してライブパーティションをフォレンジックソフトウェアで検査する場合にのみ問題になります。

コマンドが終了した直後にファイルを読みたいのですが、空のファイルを読みたくありません。

心配しないでください。カーネルは、ファイルが開かれる頻度に関係なく、ファイルのビューを1つだけ保持します。

3
Deduplicator

それとも、コマンド出口とファイルに書き込まれたデータとの間にまだ非常に小さいウィンドウがありますか?

いいえ、ありません。

コマンドが終了した直後にファイルを読みたいのですが、空のファイルを読みたくありません。

コマンドが終了した直後にファイルの最終的な内容を読むことができます。代わりに空のファイルを読むことは決してありません。 (CおよびC++では、 wait waitpid wait3 または wait4 のいずれかのシステムコールを使用して、プログラムが終了するのを待ってからファイルを読み取ってください。シェル、他のプログラミング言語、またはライブラリ(例えばCライブラリ呼び出し system またはJava Process class)を使用しているのであれば、おそらく既にこれらのシステム呼び出しの1つを使用しています。)

他の答えやコメントが指摘しているように、プログラムがその内部出力バッファをフラッシュせずに終了した場合、プログラムの終了後に空のファイルを読むことになるかもしれません(例えば _exit abort またはのため)致命的なシグナルを受信した、またはJavaプログラムが正常に終了したためです。ただし、この時点でできることは何もありません。フラッシュされていないデータは永遠に失われ、追加の待機時間では回復できません。

2
pts

一般的な規則として、カーネルが所有するすべてのデータは、カーネルperiodによって保守およびクリーンアップされます。このようなデータには、 write(2) のようなシステムコールによってカーネルメモリに転送されたデータが含まれます。

しかし、あなたのアプリケーション(例えばCライブラリ)がこれのtopでバッファリングを行っているのであれば、カーネルは明らかに何も知らないのでそのクリーンアップを保証しません。

さらに、クリーンアップのためにtimingの保証があるとは思わない。一般的に、これは「ベストエフォート」で実行される(read:)秒単位の場合).

2
Mehrdad

はい

別の余分な答えを追加してしまって申し訳ありませんが、ほとんどは質問のタイトルの赤いニシンに焦点を当てているようです。しかし私が言える限りでは、問題はバッファリングに関することではなく、これです。

コマンドの出力をファイルにリダイレクトすると(たとえば、echo Hello> file)、そのファイルはコマンド終了直後にそのようなデータを持つことが保証されますか?

はい、無条件に。あなたが記述している ">"の使い方と "|"そして "<"は、UnixおよびLinuxの世界が大きく基盤としているパイプベースの処理モデルです。すべてのLinuxインストールで、この動作に完全に依存しているのであれば、何千ものスクリプトがあるわけではありません。

それはあなたが設計通りに望みどおりに動作し、そして競合状態のわずかなチャンスさえあったなら、それはおそらく数十年前に修正されているでしょう。

0
AnoE