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