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


21

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


1
それはおそらくすぐにコマンドを実行しますが、時間の量は、それが実際にファイルを開くのにかかる、書き込み、およびクローズ等、ハードドライブ、任意の実行中のプログラムの速度と種類によって異なります
freginold

与えられた例に関して、「プロセス」とは何ですか?あるecho>ない別々の(短命)プロセス?また、echoremain before の出力はどこで>実行されますか?
oɔɯǝɹ

1
@oɔɯǝɹ >はシェルリダイレクトです。これは、プログラムが書き込みのために指定されたファイルを開いて、stdoutをシェルとまったく同じように置き換えた場合と同じです。
ダンD.

7
私はあなたを与えるために、OSの責任だと思いfile含むHelloかかわらず、それがフラッシュであるかどうかの。
サルマンA

1
プログラムがマシンAで実行されており、マシンAのファイルシステムがネットワーク経由でマウントされているマシンBでファイルを読み込んでいる場合、ネットワークファイルシステムのタイプとマウント設定に応じて、空のファイルを読み込むことになります。そのため、そのマウントのキャッシュを無効にすることができます。
pts

回答:


21

関連するバッファ/キャッシュの複数の層があります。

  1. CPUキャッシュ。

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

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

    データが収集されるプロセスにはいくつかのメモリが確保されているため、OSへのリクエストをできるだけ少なくする必要があります。これは比較的高価です。プロセスはデータをこれらのバッファにコピーしますが、これもCPUキャッシュによってバックアップされる可能性があるため、データがメインメモリにコピーされる保証はありません。アプリケーションは、たとえばfclose(3)またはfsync(3)を使用して、これらのバッファーを明示的にフラッシュする必要があります。exit(3)関数もプロセスが終了する前にこれを行いますが、_exit(2)関数はそうではありません。やっています。

  3. カーネルバッファー

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

  4. ドライブキャッシュ

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

アプリケーションでは、データをカーネルバッファーに登録するだけで十分です(実際のデータはこの時点でもCPUキャッシュに存在し、メインメモリに書き込まれていない可能性があります)。「エコー」プロセスは終了します。インプロセスバッファがフラッシュされ、データがOSに引き渡される必要があることを意味します。新しいプロセスを開始すると、要求されたときにOSが同じデータを返すことが保証されます。


7
CPUキャッシュを考慮することは私には関係がないようです。ここでは、不必要な詳細レベルです。ハードディスクプラッターまたはssdメモリのビットを表す物理量が変更されるまで、すべての詳細を確認します。
mvw

3
実際、CPUキャッシュはかなり直交しています。
サイモンリヒター

2
さらに重要なことに、CPUキャッシュはコア間で一貫しているため、完全に見えないのです。x86では、DMAと一貫性があり(x86には全記憶順序のメモリ順序モードがあります)、メモリを読み取ることができるものはすべて、メモリ操作のグローバルな順序でそのアドレスに最後に保存されたデータを参照します。(ストアキューからのストア転送により、CPUコアはグローバルに表示される前に自身のストアを認識します)。キャッシュコヒーレントDMAのない非x86プラットフォームでは、Linuxカーネルは、DMAがそれらのアドレスに到達する前にキャッシュがフラッシュされるようにします。
ピーター

1
「これらのほとんどは、アプリケーションプログラマから隠されています。」なぜ「大部分」ですか?私は組み込み開発者であり、ブートローダー(「アプリケーション」ではない)中を除き、CPUキャッシュを完全に無視します。アプリケーション開発者がCPUキャッシュの影響を受けることはないと思います。
サム

1
一部のCPUでは、@ Samキャッシュのミス/ヒットと投機的実行が悪用され、読み取りアクセス制限が回避される場合があります。おそらくこれは答えが言及しているものですか?
ジョンドヴォルザーク

22

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

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

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

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


1
「アプリケーションに内部キャッシュがない場合」—これは非常に大きな「if」です。I/ Oライブラリの実装の大部分は、デフォルトでバッファー標準出力を使用します。つまり、C標準では、たとえば、終了時にstdoutバッファーをフラッシュすることが義務付けられています(ただしexit、少なくとも暗黙的に呼び出されない場合は潜在的にそうではありません)。他のライブラリ/言語(Java!など)では、保証が少なくなります。
コンラッドルドルフ

リダイレクトプリミティブ(つまり、質問のコマンド)に制限する場合はどうなりますか?内部キャッシュはありませんよね?
エリック

@エリックいいえ、大丈夫です。
mtak

10
この答えが得られるかどうかはわかりません。問題は「プロセスが終了するとき」についてです。内部書き込みキャッシュを備えたすべてのアプリケーションは、それが以前に発生しなかった場合、プロセス終了時にディスクにフラッシュします。IOW、これらのキャッシュはここでは重要ではありません。
MSalters

2
さらに、内部バッファは、終了時にフラッシュされるか、存在から単にフェードアウトしますか?したがって、たとえ内部バッファーがフラッシュしなくても、どれだけ長く待機しても、コンテンツは観察できません。
-WorldSEnder

21

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

一般に、答えはノーです。

コマンドに依存します。他の回答が言及しているように、コマンドがデータを内部でバッファリングしない場合、コマンドが終了するとすべてのデータが利用可能になります。

しかし、すべてではありませんが、ほとんどの場合、標準I / Oライブラリデフォルトで(ある程度まで)バッファ標準出力を行い、アプリケーションの終了時のバッファの自動フラッシュについて異なる保証を提供します。

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

以下に簡単な例を示します。

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

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

これをコンパイルして実行すると、必ずしもstdoutに書き込まれるtestとは限りません

他のプログラミング言語では、さらに少ない保証しか提供していません。たとえば、Javaはプログラムの終了時に自動フラッシュしませ。出力バッファーに未終了の行が含まれている場合、System.out.flush()明示的に呼び出されない限り、その行は失われる可能性があります。

データがファイルに到着した場合:それはあなたの質問の体がわずかに異なる何かを尋ね、言ったすべてで、それはすぐにコマンドが終了(他の回答で説明した警告の対象)の後にそうする必要があります。


7
また、コマンドラインツールがデバッグログのようにファイルおよびstdoutまたはstderrに書き込みを行っているときに異常終了が発生し、ユーザーが「q」を入力して終了を減らすために「q」と入力しました。コマンドラインツールがSIGPIPEを処理しなかった場合、ディスクファイルは常に完全にフラッシュされません。
ザンリンクス

「それはすぐにそうすべき+1が、後にコマンドが終了は」非常に適切ではありません:任意のwrite()またはpwrite()システムコールはどうなる前にプロセスが終了すると、ファイルの変更が見えるようになるときです。だから、最後のファイルの変更は間違いである前に、遅くとも直前、プロセス終了。mmap(MAP_SHARED)ファイルを使用しても、すべてのファイル変更が発生する前にプロセスの終了を監視する方法はありません。
ピーター

9

まだこの問題に十分に取り組んでいる質問はないと思います。

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

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

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

  • プロセスがファイルを読み込む場合、要求されたファイル部分が現在バッファ内にある場合、カーネルはバッファの内容をプロセスに提示します。そうでない場合、カーネルは基礎となる記憶媒体からデータを取得してバッファ内に配置し、前の手順に戻ります。

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


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


2
厳密に言えば、これを保証するのはLinuxのファイルシステムのセマンティクスではなく、POSIXのセマンティクスです。特に、BSDはmacOS、さらにはWindowsとまったく同じように動作します(ただし、WindowsはPOSIXセマンティクスに従う数少ないケースの1つです)。またmmap()、これはO_DIRECTでだれも奇妙なことをしていないことを前提としているため、ディスクとページキャッシュ間で同期が取れなくなる可能性があります(ただし、プロセスが終了する瞬間は解決されます)。
オースティンヘメルガルン

2
@AustinHemmelgarn:厳密にLinuxのを念頭に置いてのUnix(System Vの)アプリケーションをサポートして設計され、後にもシステムV.上の多くの概念を基礎にPOSIXをサポートするために作られたので、我々は両方の正しい話す
デビッド・フェルスター

5

コマンドがCランタイムライブラリを使用するプログラムによって実行されると仮定すると、ある時点で呼び出しfcloseて開いているファイルを閉じる必要があります。

fcloseC関数のmanページには次のように書かれています:

注意fclose()は、Cライブラリが提供するユーザー空間バッファのみをフラッシュすることに注意してください。データがディスクに物理的に保存されるようにするには、たとえばsync(2)またはfsync(2)を使用して、カーネルバッファーもフラッシュする必要があります。

のmanページにfflushも同じメモがあります。のmanページにcloseは次のように書かれています:

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

データはドライブと同期されていなくても、他のプロセスで使用できることに注意してください。多分それはあなたにとって既に十分であるかもしれません。

疑問がある場合は、テストを作成してください。


2
Cであるかどうかにかかわらず、すべてがclose()ファイルの記述子を閉じるためにsyscallを使用する必要があります。
Attie

@Attie:終了する前にファイルを作成する必要ありませんclose(エラーをチェックしないハックプログラムの場合)。カーネルはそれらをクリーンアップし、closeプロセスの終了後に効果的に呼び出します。fcloseただし、exit(3)出口システムコールを直接呼び出すのではなく、バッファリングされたstdioストリームが必要です。
ピーター

疑問がある場合は、テストを作成してください。 これは、競合状態を検出するための悪いアドバイスです。1つのハードウェア上で実行されている1つのカーネルでテストを行うと、そのシステムでのテストによって生成されたソフトウェア条件ではレースを実行できないか、または検出するのが非常にまれであることがわかります。しかし、その動作がすべてのファイルシステム、カーネル、およびすべてのハードウェア(PowerPCなど)で安全であると思われるかどうかはわかりません。つまり、依存している保証が実装の詳細なのか、意図的な将来保証であるのかを判断することはできません。(この場合はそうです。)
ピーター

それは状況次第です。このアドバイスは、シェルスクリプトを実行しようとする人々の助けになるかもしれません。これは、OSカーネルで作業するソフトウェアエンジニア、Intelのマイクロコードアップデートで作業している人、ISSのシステムで作業をしている一部のギャルなど、より高度ではあるがあまりありそうもない環境に対する一般的なソリューションとして意図されていませんでした。
mvw

3

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

はい。シェルは出力ファイルを開き、echoそこに直接出力します。コマンドが終了すると、完了です。

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

データがすでにメディア上にあるかどうかは別の問題であり、それはその後ハードウェア障害が発生した場合にのみ問題になります。

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

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


「カーネルはファイルの1つのビューのみを保持します」:まったく当てはまりませんmmap(MAP_SHARED):mmaped領域へのストアは、ファイルの読み取り(そのスレッドまたは他のプロセスによる)と一貫性がありません。これがmsync(2)存在する理由です。少なくともそれはmanページが警告していることです。実装に応じて、Linuxは実際にページキャッシュから物理ページをマップする場合があります。その場合、基本的に一貫性があると思います(モジュロメモリ順序付け)。とにかく、それはまだすべて前に発生し_exit(2)ます。
ピーター

2

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

ただし、アプリケーション(たとえば、Cライブラリ)がこのでバッファリングを実行する場合、カーネルは明らかにわからないため、クリーンアップを保証しません。

さらに、クリーンアップのタイミング保証はないと思います。一般に、「ベストエフォート」(「秒があるとき」を参照)ベースで実行されます。


waitpid()クリーンアップが行われた場合、親プロセスが戻る前にクリーンアップ/バッファフラッシュが行われるという保証があります。つまり、他のプロセスは、そのプロセスによって行われたファイル変更の前に発生するプロセスの終了を直接観察することはできません。(NFSキャッシングはホスト間で完全に一貫していないため、NFSファイルのタイムスタンプを介した間接的な観察を除外するために「直接」と言いました。)
Peter Cordes

@PeterCordes:「維持」ではなく「クリーンアップ」の意味に依存すると思います。私にとって「維持する」とは「一貫したビューを提供する」(あなたが言及した保証がある)であり、「クリーンアップ」とは「ディスクにフラッシュする」ことであり、タイミング保証はないと思われる。
Mehrdad

ああ、あなたは質問の「ディスクにフラッシュされた」部分に答えているのですが、これはファイルを読んだときに後のプロセスが見るものとは無関係です。「ダーティI / Oキャッシュ/バッファメモリをクリーンにする」という意味での「クリーンアップ」。fsync/ を使用しない限り、タイミング保証はありませんがfdatasync、Linuxでのバッファライトバックは/proc/sys/vm/dirty_writeback_centisecs100分の1秒後に開始されます(他のI / Oトラフィックによって遅延されない場合)。ライトバックを行う前にバッファを大きくするために大きい)。
ピーター

2

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

いいえ、ありません。

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

コマンドの終了直後にファイルの最終的な内容を読み取ることができますが、代わりに空のファイルを読み取ることはありません。(CおよびC ++では、waitwaitpidwait3またはwait4システムコールを使用して、プログラムが終了するのを待ってからファイルを読み取ります。シェル、別のプログラミング言語またはライブラリ(Cライブラリなど)を使用している場合システムまたはJava プロセスクラスを呼び出します)、おそらくこれらのシステムコールのいずれかを既に使用しています。)

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


0

はい

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

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

はい、無条件に。説明している「>」の使用方法と「|」「<」は、UnixおよびLinuxの世界が大きく基盤としているパイプベースの処理モデルです。すべてのLinuxインストールで、この動作に完全に依存する数千個ではないにしても、数百個のスクリプトがあります。

デザインごとに希望どおりに機能します。競合状態が発生する可能性がわずかでもあった場合、おそらく数十年前に修正されていました。


残念ながら、これは不要です。不揮発性ストレージにデータをコミットするというレッドヘリングに焦点を当てているのは、ほんの2、3の答えだけです。明確な説明については、@ ptsの答えと他のいくつかを参照してください。ファイルの変更は終了前に行われるか、まったく行われません。
ピーター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.