web-dev-qa-db-ja.com

ファイルアクセスにmmapを使用する必要があるのはいつですか?

POSIX環境では、少なくとも2つの方法でファイルにアクセスできます。標準システムコールopen()read()write()、およびフレンドがありますが、mmap()を使用してファイルを仮想メモリにマップするオプションもあります。

どちらを使用するのが望ましいのですか? 2つのインターフェイスを含むメリットがある個々の利点は何ですか?

256
Peter Burns

同じファイルから読み取り専用でデータにアクセスする複数のプロセスがある場合、mmapは素晴らしいです。これは、私が書いているサーバーシステムの種類で一般的です。 mmapを使用すると、これらすべてのプロセスで同じ物理メモリページを共有でき、多くのメモリを節約できます。

mmapにより、オペレーティングシステムはページング操作を最適化することもできます。たとえば、2つのプログラムを考えます。 mallocで作成されたバッファーに1MBファイルを読み込むプログラムAと、1MBファイルをメモリーにmmapするプログラムB。オペレーティングシステムがAのメモリの一部をスワップアウトする必要がある場合、メモリを再利用する前に、スワップするバッファの内容を書き込む必要があります。 Bの場合、変更されていないmmapされたページは、OSがmmapされた既存のファイルからそれらを復元する方法を知っているため、すぐに再利用できます。 (OSは、書き込み時コピー戦略と同様に、書き込み可能なmmapされたページを最初に読み取り専用としてマークし、segフォールトをキャッチすることにより、変更されていないページを検出できます)。

mmapはプロセス間通信にも役立ちます。通信する必要のあるプロセスでファイルを読み取り/書き込みとしてmmapし、mmapされた領域で同期プリミティブを使用できます(これがMAP_HASSEMAPHOREフラグの目的です)。

32ビットマシンで非常に大きなファイルを操作する必要がある場合は、mmapが扱いにくい場合があります。これは、mmapがプロセスのアドレス空間で、マップされるファイルの全範囲に適合するのに十分な大きさの連続したアドレスブロックを見つけなければならないためです。これは、アドレススペースが断片化され、2 GBのアドレススペースが空いている場合に問題になる可能性がありますが、1 GBのファイルマッピングに適合する個々の範囲はありません。この場合、ファイルを適切なサイズにマップする必要があります。

読み取り/書き込みの代替としてmmapを使用する場合のもう1つの不便な点は、ページサイズのオフセットでマッピングを開始する必要があることです。オフセットXでデータを取得する場合は、mmapと互換性があるようにそのオフセットを修正する必要があります。

最後に、読み取り/書き込みが唯一の方法ですcanいくつかの種類のファイルを操作します。 mmapは、パイプやttyのようなものには使用できません。

284
Don Neufeld

Mmap()が利点ではないとわかった1つの領域は、小さなファイル(16K未満)を読み取るときでした。ファイル全体を読み取るためのページフォールトのオーバーヘッドは、単一のread()システムコールを実行する場合に比べて非常に高くなりました。これは、タイムスライス内でカーネルが読み取りを完全に満たすことがあり、コードが切り替わらないことがあるためです。ページフォールトでは、別のプログラムがスケジュールされる可能性が高くなり、ファイル操作の待ち時間が長くなります。

62
Ben Combee

mmapには、大きなファイルにランダムアクセスがある場合に利点があります。もう1つの利点は、バッファリングに煩わ​​されることなく、メモリ操作(memcpy、ポインター演算)でアクセスできることです。通常のI/Oは、バッファよりも大きな構造がある場合にバッファを使用すると、非常に困難になる場合があります。コードを正しく処理するのは難しい場合が多く、mmapは一般に簡単です。これは、mmapを操作するときに特定のトラップが存在するということです。すでに述べたように、mmapはセットアップに非常にコストがかかるため、特定のサイズ(マシンごとに異なる)でのみ使用する価値があります。

ファイルへの純粋な順次アクセスの場合、madviseを適切に呼び出すことで問題を軽減できる場合もありますが、常に良い解決策とは限りません。

アーキテクチャのアライメント制限(SPARC、itanium)に注意する必要があります。読み取り/書き込みIOを使用すると、多くの場合、バッファーは適切にアライメントされ、キャストされたポインターを逆参照するときにトラップしません。

また、マップの外部にアクセスしないように注意する必要があります。マップ上で文字列関数を使用し、ファイルの末尾に\ 0が含まれていない場合、簡単に発生します。ファイルサイズがページサイズの倍数でない場合、ほとんどの場合に機能します。最後のページは0で埋められます(マップ領域は常にページサイズの倍数のサイズになります)。

43

他の素敵な答えに加えて、Googleの専門家Robert Loveが書いた Linuxシステムプログラミング からの引用:

mmap( )の利点

mmap( )を介したファイルの操作には、標準のread( )およびwrite( )システムコールに比べていくつかの利点があります。それらの中には:

  • メモリマップファイルの読み取りと書き込みにより、read( )またはwrite( )システムコールを使用するときに発生する余分なコピーを回避します。この場合、データはユーザースペースバッファーとの間でコピーする必要があります。

  • 潜在的なページフォールトは別として、メモリマップトファイルの読み取りと書き込みでは、システムコールやコンテキストスイッチのオーバーヘッドは発生しません。メモリにアクセスするのと同じくらい簡単です。

  • 複数のプロセスが同じオブジェクトをメモリにマップすると、データはすべてのプロセス間で共有されます。読み取り専用および共有の書き込み可能なマッピングは完全に共有されます。書き込み可能なプライベートマッピングには、まだCOW(コピーオンライト)ページが共有されています。

  • マッピングを探索するには、簡単なポインター操作が必要です。 lseek( )システムコールは不要です。

これらの理由から、mmap( )は多くのアプリケーションにとって賢明な選択です。

mmap( )の欠点

mmap( )を使用する際に留意すべき点がいくつかあります。

  • メモリマッピングは常に整数のページ数です。したがって、バッキングファイルのサイズと整数のページ数の差は、スラックスペースとして「無駄」になります。小さなファイルの場合、マッピングのかなりの割合が無駄になる可能性があります。たとえば、4 KBページでは、7バイトのマッピングは4,089バイトを無駄にします。

  • メモリマッピングは、プロセスのアドレス空間に収まる必要があります。 32ビットのアドレス空間では、非常に多数のさまざまなサイズのマッピングにより、アドレス空間が断片化され、大きな空きの連続した領域を見つけることが難しくなります。もちろん、この問題は64ビットのアドレス空間ではそれほど顕著ではありません。

  • カーネル内のメモリマッピングおよび関連するデータ構造の作成と保守にはオーバーヘッドがあります。このオーバーヘッドは、特に大きく頻繁にアクセスされるファイルの場合は特に、前のセクションで説明した二重コピーを削除することで一般的に不要になります。

これらの理由から、mmap( )の利点は、マップされたファイルが大きい場合(したがって、無駄なスペースがマッピング全体のわずかな割合である場合)、またはマップされたファイルの合計サイズが均等に割り切れる場合に最も大きく実現されますページサイズによって(したがって、無駄なスペースはありません)。

20
Miljen Mikic

メモリマッピングには、従来のIOと比較して速度が大幅に向上する可能性があります。メモリマップファイルのページがタッチされると、オペレーティングシステムがソースファイルからデータを読み取ることができます。これは、フォールトページを作成することで機能します。フォールトページは、OSが検出し、OSがファイルから対応するデータを自動的に読み込みます。

これは、ページングメカニズムと同じように機能し、通常、システムページの境界とサイズ(通常4K)でデータを読み取ることにより、高速I/O向けに最適化されます。これは、ほとんどのファイルシステムキャッシュが最適化されるサイズです。

10
grover