MMAP(2)

名称
mmap - メモリの割り当て、またはファイルまたはデバイスのメモリへのマップ

ライブラリ
libc

書式
<sys/mman.h> void * mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)

解説
mmap() システムコールは、 addr を始点として最大で len バイトの連続するページに、 fd によって記述されるオブジェクトのバイトオフセット offset の位置から始まる部分をマップされるようにします。 len がページサイズの倍数でない場合、 マップされた領域は指定の範囲を越えて拡張されるかもしれません。 このような拡張によってマップされたオブジェクトの末端を越えた部分は 0 で埋められます。

addr が 0 でない場合、これはシステムへのヒントとして使用されます (システムの便宜のために、領域の実際のアドレスは 指定されたアドレスとは違う可能性があります)。 addr が 0 の場合、アドレスはシステムによって選択されます。 領域の実際の開始アドレスが返されます。 mmap が処理に成功すると、確保されたアドレス範囲の以前のマッピングは 削除されます。

保護 (領域へのアクセス許可) は prot 引数で、以下の値の論理和 (or) を取った値で指定します:

PROT_NONE
    ページはアクセスできません。
PROT_READ
    ページは読取りできます。
PROT_WRITE
    ページは書込みできます。
PROT_EXEC
    ページは実行可能です。

flags 引数は、マップされたオブジェクトのタイプ、マッピングオプション、 およびマップされたページのコピーに対して行われた修正が、プロセスに 固有であるかまたは他からの参照と共有されるかを指定します。 共有、マッピングタイプ、およびオプションは、以下の値の論理和 (or) を取った値で flags 引数に指定します:

MAP_ANON
    どの特定のファイルとも対応していない匿名メモリをマップします。 MAP_ANON を作成するのに使用されるファイル記述子は -1 である必要があります。 offset 引数は無視されます。

MAP_FIXED
    システムが、指定されたアドレスと異なるアドレスを選択することを許容しません。 指定されたアドレスが使用できない場合、 mmap() は処理に失敗します。 MAP_FIXED が指定されている場合、 addr はページサイズの倍数である必要があります。 このオプションの使用はお勧めできません。

MAP_HASSEMAPHORE
    領域にセマフォが含まれている可能性があること、特殊な処理が必要な可能性が あることをカーネルに通知します。

MAP_INHERIT
    このフラグは正式に公表されたものとして扱われたことはなく、 もはやサポートされていません。 さらなる情報は minherit(2) を参照してください。

MAP_NOCORE
    領域はコアファイルに含まれません。

MAP_NOSYNC
    はこの VM マップを経由して汚されたデータを、無闇にではなく (通常はページャによって) 必要な時のみ物理的なメディアに フラッシュするようにします。 普通、このオプションにより、更新デーモンはこのマップで 汚されたページをフラッシュしないようになります。 これにより、ファイルバックアップされた共有メモリのマッピングを使用して 無関係なプロセスの間でメモリアクセスを効率的に共有することが できるようになります。 このオプションがないと、汚された VM ページは頻繁 (通常 30-60 秒毎) に ディスクにフラッシュされるかも知れず、そのような動作を必要としない場合 (例えば IPC のためにファイルを用いた共有 mmap 領域を用いている場合) パフォーマンスに問題が出ることがあります。 MAP_NOSYNC を使っているかにかかわらず、 VM/ ファイルシステムの一貫性は保たれることに注意してください。 このオプションは UNIX プラットフォーム間で (まだ) 移植性はありませんが、 いくつかのプラットフォームではデフォルトで同じ動作をするように 実装されているかも知れません。

    警告! ftruncate(2) を使いファイルを拡張してから、つまりファイルに大きな穴を空けてから、 その穴を共有 mmap() を修正して埋める場合、深刻なファイル断片化が生じる可能性があります。 この断片化を避けるために、 mmap() でその領域を修正する前に、新規に拡張した領域に 0 を write() して、ファイルのバッキングストアを事前に割り当てておく必要があります。 ディスクへのフラッシュが全くランダムに生じるため、 断片化問題に特に敏感なのは、 MAP_NOSYNC ページです。

    同じことが、 MAP_NOSYNC を使いファイルベースの共有メモリストアを実装する場合にも言えます。 ftruncate() してバッキングストアを作るのではなく、0 を write() してバッキングストアを作ることを推奨します。 たとえば、 “dd if=filename of=/dev/null bs=32k” を使うなどして巨大なファイルをシーケンシャルに読み取りながら、 “iostat 1” を呼び出すことで得られる KB/t (転送 1 回あたりのキロバイト数) を 観察することでファイル断片化のテストが可能です。

    fsync(2) システムコールはすべての汚染されたデータとファイルに関連づけられたメタデータ (NOSYNC の汚れた VM データを含む) を物理的媒体にフラッシュします。 sync(8) コマンドと sync(2) システムコールは、汚染された NOSYNC VM のデータを通常フラッシュしません。 msync(2) システムコールは BSD で整合性のあるファイルシステムのバッファキャッシュが実装されたので 廃止されました。 しかしながら、汚れた VM ページとファイルシステムを結びつけ、 物理的媒体にすぐに (後程ではなく) フラッシュさせる用途に 使われることもあります。

MAP_PRIVATE
    修正はプロセス固有に行われます。

MAP_SHARED
    修正は共有されます。

MAP_STACK
    MAP_STACK は MAP_ANON および 0 の offset 指定を含みます。 fd 引数は -1 でなければならず、 prot には少なくとも PROT_READ と PROT_WRITE が入っている必要があります。 このオプションは、スタックの先頭を開始点とし下方に伸びる、サイズが最大で len バイトまで伸びるメモリ領域を作成します。 スタックの先頭は、呼び出しから返された開始アドレスに len バイトを加えたものになります。 最も伸びた場合のスタックの下端は、 呼び出しによって返される開始アドレスになります。

close(2) システムコールはページをアンマップしません。 詳細については munmap(2) を参照してください。

現在の設計ではプロセスはスワップ空間の位置を指定できません。 将来は、追加のマッピングタイプ MAP_SWAP を定義するかもしれません。 この場合、ファイル記述子引数には スワップを行うべきファイルまたはデバイスを指定します。

戻り値
正常に完了すると、 mmap() は、マップされた領域を指すポインタを返します。 そうでない場合は 値 MAP_FAILED が返され、エラーを示すために errno が設定されます。

エラー
mmap() システムコールは次の場合に失敗します:

[EACCES]
    フラグ PROT_READ が prot 引数の一部として指定されましたが、 fd が読取り用に開かれていませんでした。 フラグ MAP_SHARED と PROT_WRITE が flags と prot 引数の一部として指定されましたが、 fd は書込み用に開かれていませんでした。

[EBADF]
    fd 引数が有効な開かれたファイルの記述子ではありません。

[EINVAL]
    MAP_FIXED が指定されて addr 引数がページ境界に整列されていないか、または指定の アドレスの一部がユーザプロセスの有効なアドレス空間の外になります。

[EINVAL]
    len 引数が負でした。

[EINVAL]
    MAP_ANON が指定されて fd 引数が -1 ではありませんでした。

[EINVAL]
    MAP_ANON が指定されておらず、 fd が通常のファイルまたはキャラクタ型特殊ファイルを参照していませんでした。

[EINVAL]
    offset 引数がページ境界に整列していませんでした (後述する「 バグ の章」を参照)。

[ENOMEM]
    MAP_FIXED が指定されていますが、 addr 引数が与えられていません。 MAP_ANON が指定されて利用できるメモリが不充分でした。 sysctl 値 vm.nax_proc_mmap で指定されたプロセス毎の mmap 限界に達しました。

関連項目
madvise(2), mincore(2), minherit(2), mlock(2), mprotect(2), msync(2), munlock(2), munmap(2), getpagesize(3), make.conf(5)

バグ
len 引数は 2GB に限定されます。 2GB をわずかに上回るマッピングは機能しませんが、 2GB, 4GB, 6GB および 8GB よりわずかに少ないファイルサイズについて (ファイルサイズ % 2GB) のサイズのウィンドウをマップできます。

制約は多彩な理由から生じています。 そのほとんどは、パフォーマンス上の著しいペナルティのため、 FreeBSD では VM システム内で 64 ビットのオフセットを使用したくないことと 関係しています。 したがって FreeBSD は 32 ビットのページインデックスを使用しており、これによって FreeBSD では最高で 8TB までのファイルサイズを利用できます。 実際にはさらに制約が課されて使用可能サイズは 1TB までですが、 これは、ファイルシステムコード内のバグによるものです (ブロック番号計算を行っているときの桁落ち)。

2GB 制限のもうひとつの理由は、ファイルシステムメタデータが負のオフセットに 存在できるということです。

0 バイトを mmap() しようとしても何の効果もなく、単に成功します。 一方、0 バイトを munmap() しようとすると [EINVAL] が返されることに注意してください。