一般に、UNIXで複数のプロセスからファイルに追加する場合、何を当然のことと見なすことができますか?データを失う可能性はありますか(一方のプロセスが他方の変更を上書きします)?データが壊れる可能性はありますか?(たとえば、各プロセスはログファイルに追加ごとに1行を追加しますが、2行がマングルされる可能性はありますか?)追加が上記の意味でアトミックでない場合、相互排除を保証する最良の方法は何ですか?
4 に答える
'PIPE_BUF'のサイズ未満の書き込みは、アトミックであると想定されています。これは少なくとも512バイトである必要がありますが、簡単に大きくなる可能性があります(Linuxでは4096に設定されているようです)。
これは、すべての完全にPOSIX準拠のコンポーネントについて話していることを前提としています。たとえば、これはNFSには当てはまりません。
ただし、「O_APPEND」モードで開いたログファイルに書き込み、行(改行を含む)を「PIPE_BUF」バイト長に保つと仮定すると、破損の問題なしにログファイルに複数のライターを作成できるはずです。割り込みは、書き込みの途中ではなく、書き込みの前または後に到着します。再起動後もファイルの整合性を維持したい場合はfsync(2)
、書き込みのたびに呼び出す必要がありますが、パフォーマンスにとってはひどいものです。
明確化:コメントとオズソロモンの答えを読んでください。それがそのサイズの原子性O_APPEND
を持っていると思われるかどうかはわかりません。PIPE_BUF
Linuxがどのように実装されたかwrite()
、または基盤となるファイルシステムのブロックサイズが原因である可能性は十分にあります。
編集: 2017 年 8 月に最新の Windows の結果を更新しました。
非同期ファイルシステムとファイル I/O C++ ライブラリを実装する提案されたBoost.AFIOの作成者として、テスト コードと結果へのリンクを含む回答を提供します。
まず、Windows の O_APPEND または同等の FILE_APPEND_DATA は、最大ファイル エクステント (ファイルの「長さ」) の増分が同時書き込みではアトミックであることを意味します。これは POSIX によって保証されており、Linux、FreeBSD、OS X、および Windows はすべて正しく実装しています。Samba もこれを正しく実装していますが、v5 より前の NFS はアトミックに追加するワイヤ フォーマット機能がないため実装していません。そのため、ファイルを追加のみで開いた場合、NFS が関係していない限り、主要な OS で同時書き込みが相互に破棄されることはありません。
ただし、アトミック追加への同時読み取りでは、OS、ファイリング システム、およびファイルを開いたフラグによっては、書き込みの欠落が発生する場合があります。最大ファイル エクステントの増分はアトミックですが、読み取りに関する書き込みの可視性は、場合によってはそうでない場合もあります。アトミックであること。フラグ、OS、ファイリング システムごとの簡単な要約を以下に示します。
O_DIRECT/FILE_FLAG_NO_BUFFERING なし:
NTFS を使用する Microsoft Windows 10: 更新原子性 = 10.0.10240 までは 1 バイト、10.0.14393 からは少なくとも 1Mb、おそらく無限 (*)。
ext4 を使用する Linux 4.2.6: アトミック性を更新 = 1 バイト
FreeBSD 10.2 with ZFS: 更新原子性 = 少なくとも 1Mb、おそらく無限 (*)
O_DIRECT/FILE_FLAG_NO_BUFFERING:
NTFS を使用する Microsoft Windows 10: update atomity = 10.0.10240 まで (10.0.10240 を含む)、ページが整列されている場合のみ最大 4096 バイト、それ以外の場合は FILE_FLAG_WRITE_THROUGH がオフの場合は 512 バイト、それ以外の場合は 64 バイト。この原子性は、おそらく設計されたものではなく、PCIe DMA の機能であることに注意してください。10.0.14393 以降、少なくとも 1Mb、おそらく無限 (*) です。
ext4 を使用する Linux 4.2.6: 更新原子性 = 少なくとも 1Mb、おそらく無限 (*)。ext4 を使用する初期の Linux では 4096 バイトを超えなかったことに注意してください。XFS では確かにカスタム ロックが使用されていましたが、最近の Linux ではこれが最終的に修正されたようです。
FreeBSD 10.2 with ZFS: 更新原子性 = 少なくとも 1Mb、おそらく無限 (*)
生の経験的テスト結果はhttps://github.com/ned14/afio/tree/master/programs/fs-probeで確認できます。512 バイトの倍数でのみ引き裂かれたオフセットをテストすることに注意してください。そのため、512 バイト セクターの部分的な更新が読み取り-変更-書き込みサイクル中に引き裂かれるかどうかはわかりません。
したがって、OP の質問に答えるために、O_APPEND 書き込みは互いに干渉しませんが、O_APPEND 書き込みと同時の読み取りでは、O_DIRECT がオンになっていない限り、おそらく ext4 を使用する Linux で破損した書き込みが見られます。その場合、O_APPEND 書き込みはセクター サイズの倍数である必要があります。
(*) 「おそらく無限」は、POSIX 仕様の次の節に由来します。
次のすべての関数は、通常のファイルまたはシンボリック リンクで動作する場合、POSIX.1-2008 で指定された効果において相互にアトミックである必要があります ... [多くの関数] ... read() ... write( ) ... 2 つのスレッドがそれぞれこれらの関数の 1 つを呼び出す場合、各呼び出しは、他の呼び出しの指定された効果をすべて見るか、またはそれらのどれも見ないかのいずれかになります。[ソース]
と
書き込みは、他の読み取りおよび書き込みに対してシリアル化できます。ファイル データの read() がデータの write() の後に発生することが (何らかの方法で) 証明できる場合は、呼び出しが異なるプロセスによって行われた場合でも、その write() を反映する必要があります。[ソース]
しかし逆に:
このボリュームの POSIX.1-2008 では、複数のプロセスからファイルへの同時書き込みの動作は指定されていません。アプリケーションでは、なんらかの形式の同時実行制御を使用する必要があります。[ソース]
標準の内容は次のとおりです: http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html。
ファイル
O_APPEND
ステータス フラグのフラグが設定されている場合、ファイル オフセットは各書き込みの前にファイルの末尾に設定され、ファイル オフセットの変更と書き込み操作の間に介在するファイル変更操作は発生しません。