Linuxのmount(2)
manページで、次の抜粋に気づきました。
多くの壊れたアプリケーションは、次のようなパターンを介して既存のファイルを置き換えるときにfsync()を使用しません
_fd = open("foo.new")/write(fd,...)/close(fd)/ rename("foo.new", "foo")
_またはさらに悪い
_fd = open("foo", O_TRUNC)/write(fd,...)/close(fd).
_Auto_da_allocが有効になっている場合、ext4はreplace-via-renameおよびreplace-via-truncateパターンを検出し、次のジャーナルコミット時に、デフォルトのdata = orderedモードで、のデータブロックが割り当てられるように遅延割り当てブロックを強制的に割り当てます。新しいファイルは、rename()操作がコミットされる前に強制的にディスクに入れられます。これにより、ext3とほぼ同じレベルの保証が提供され、遅延割り当てブロックがディスクに強制される前にシステムがクラッシュしたときに発生する可能性がある「長さがゼロ」の問題が回避されます。
このコードはどのような意味で「壊れている」のでしょうか。彼らは、このコードが違法であるか、標準に準拠していない(POSIXなど)と言っていますか?
明らかに、fsync()
は、システムがクラッシュした場合に何が起こるかを心配している人にとっては良い考えかもしれません。しかし、システムがクラッシュしないと仮定すると、fsync()
なしで、サンプルコードの両方のバージョンが正確に正しいことをしませんか?
rename
はアトミックであることが期待されます:完全に完了するか、まったく完了しないかのいずれかです。 Bの代わりにAの名前を変更すると、AとBの両方がそのまま残ることになります(まったく発生しませんでした)。または、Bという名前のAのコンテンツのみを使用します(完全に完了しました)。
システムがクラッシュしない限り、fsync
(など)の呼び出しに関係なく発生します。
ただし、システムがクラッシュした場合は、名前の変更自体がディスクにヒットする(したがって完了する)ことが判明する可能性があります。名前は!=ファイルであることを忘れないでください。ファイル/ iノードには複数の名前を付けることができます。名前の変更は、基になるファイル/データではなく、名前を変更します。
したがって、プログラムがAを書き込み、Bの代わりに名前を変更した後、電源が切れた状態にすることができます。ファイルシステムが名前をディスクに書き込んだことが判明しましたが、Aの実際のデータは書き込まれていません。fsync
なしで行う必要はありません。したがって、長さがゼロのB、または塗りつぶされていないBになります。
アプリがファイルを単に上書きするのではなく、書き込み-一時ファイル+名前変更を行う理由は、それがwantsクラッシュの安全性のためです。重要なドキュメントの半分書かれた一時的なコピーが、変更されていない適切なコピーの隣に置かれていれば、ユーザーはそれほど怒っていません。しかし、良いコピーが残っていない場合、ユーザーは満足しません。
コードは合法ですが「ナイーブ」です。問題はまさにクラッシュ中に起こることの問題です
ディレクトリが更新される前に新しいデータにスペースが割り当てられない可能性があるため、データが失われるリスクがあります。
優れたアプリは、fflush()
とfsync()
を呼び出して、データがディスクにフラッシュされるようにします。 auto_da_alloc
ルーチンは、カーネルでこれをヒューリスティックに実行しようとする試みです。
https://bugzilla.kernel.org/show_bug.cgi?id=103111#c1 いくつかの「落とし穴」について説明しています。
完全に合法である場合、それは機能しますが、あなたが望むことをしません。
2番目は明らかです。新しいものを保存する前に、元のファイルを破棄します。
1つ目はあまり明白ではなく、システム障害(停電など)があった場合は、何もしなかった(開始されなかった)ように見えます。古いものと新しいものの2つのファイルがあります。または成功しました。ただし、fsync
と書かれていることを行わない限り、これは当てはまりません。
彼らはあなたの注意を引くために壊れた言葉を使っています。ユーザーがデータを失うため、壊れています。今日ではないかもしれません。多分明日ではありませんが、すぐにそしてその人生の残りの間。
これはいわゆる断続的なバグです。症状が現れるまで何年も続く可能性のあるバグです。
ユーザーのデータ整合性を気にしないのであれば、なぜ最初の例を実行するのですか。