mmap, munmap -
ファイルやデバイスをメモリーにマップ/アンマップする
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
機能検査マクロの要件に関する情報は「注意」の節を参照。
mmap()
は、新しいマッピングを呼び出し元プロセスの仮想アドレス空間に作成する。
新しいマッピングの開始アドレスは
addr
で指定される。マッピングの長さは
length
引き数で指定される。
addr が NULL
の場合、カーネルがマッピングを作成するアドレスを選択する。
この方法は最も移植性のある新しいマッピングの作成方法である。
addr が NULL
でない場合、カーネルはマッピングをどこに配置するかのヒントとして
addr を使用する。Linux
では、マッピングはすぐ近くのページ境界に作成される。
新しいマッピングのアドレスは、呼び出しの返り値として返される。
ファイルマッピングの内容は、
ファイルディスクリプター
fd
で参照されるファイル
(もしくは他のオブジェクト)
のオフセット
offset
から開始される
length
バイトのデータで初期化される
(ファイルマッピングは無名マッピングの反対語である。
MAP_ANONYMOUS を参照)。
offset は
sysconf(_SC_PAGE_SIZE)
が返すページサイズの倍数でなければならない。
引き数
prot
には、マッピングのメモリー保護をどのように行なうかを指定する
(ファイルのオープンモードと矛盾してはいけない)。
prot には、
PROT_NONE
か、以下のフラグをひとつ以上ビット毎の論理和
(OR) をとったものを
指定できる。
- PROT_EXEC
- ページは実行可能である。
- PROT_READ
- ページは読み込み可能である。
- PROT_WRITE
- ページに書き込み可能である。
- PROT_NONE
- ページにはアクセスできない。
flags
引き数により、マッピングに対する更新が同じ領域をマッピングしている
他のプロセスに見えるか、更新がマッピング元のファイルを通じて
伝えられるか、が決定される。この動作は、以下の値のいずれか一つだけ
(複数は指定できない)
を
flags
に含めることで指定する。
- MAP_SHARED
- このマッピングを共有する。
マッピングに対する更新はこのファイルをマッピングしている他のプロセス
から見える。更新はマッピング元のファイルを通じて伝えられる。
ただし、ファイルの実際の更新は
msync(2) または munmap()
が呼ばれるまで行われないこともある。
- MAP_PRIVATE
- プライベートな
copy-on-write
(書き込み時コピー)
マップを生成する。
マッピングに対する更新は同じファイルをマッピングしている他のプロセス
には見えず、更新がマッピング元のファイルを通じて伝えられることもない。
mmap()
の呼び出し後にマッピング元のファイルに対して行われた変更が、
マップ領域に反映されるかどうかは規定されていない。
上記の二つのフラグは
POSIX.1-2001
で規定されている。
さらに、以下の値のうち
0
個以上をビット毎の論理和
(OR) で
flags
に指定することができる。
-
MAP_32BIT (Linux 2.4.20, 2.6 以降)
- マッピングをプロセスのアドレス空間の先頭
2
ギガバイト以内に配置する。
このフラグがサポートされているのは
x86-64
アーキテクチャー上の
64
ビットプログラムのみである。
このフラグが追加されたのは、スレッドのスタックをメモリーの先頭
2GB 以内の
どこかに割り当てることで、初期のいくつかの
64
ビットプロセッサにおける
コンテキストスイッチの性能問題を改善するためである。
最近の x86-64
プロセッサではこの性能問題はもはや存在せず、
そのようなシステムではこのフラグを使用する必要はない。
MAP_FIXED
がセットされている場合は、
MAP_32BIT
フラグは無視される。
- MAP_ANON
-
MAP_ANONYMOUS
の同義語。非推奨。
- MAP_ANONYMOUS
- マッピングはどのファイルとも関連付けされない。
マッピングの内容は 0
で初期化される。
引き数 fd と offset
は無視される。
ただし、実装によっては
MAP_ANONYMOUS (もしくは MAP_ANON)
が指定された場合、
fd を -1
にする必要があり、
移植性が必要なアプリケーションでは必ず
fd を -1
にすべきである。
MAP_ANONYMOUS と MAP_SHARED
を組み合わせての利用は
カーネル 2.4 以降の Linux
でのみサポートされている。
- MAP_DENYWRITE
- このフラグは無視される
(ずっと前は、マップ元のファイルへの書き込みを行おうとすると、エラー
ETXTBUSY
で失敗するようにシグナルが設定されていたが、これは
denial-of-service (サービス拒否)
攻撃の原因となった)。
- MAP_EXECUTABLE
- このフラグは無視される。
- MAP_FILE
- 互換性のためのフラグ。無視される。
- MAP_FIXED
-
addr
をアドレスのヒントとして使用するのではなく、
addr
で指定されたアドレスをそのまま使用してマッピングを配置する。
addr
はページサイズの倍数でなければならない。
addr と len
で指定されたメモリー領域が既存のマッピングのページと重なる場合、
既存のマッピングの重なった部分は捨てられる。
もし指定されたアドレスが使用できない場合、
mmap() は失敗する。
マッピングに対して固定アドレスを要求するのは移植性の面で劣るので、
このオプションは使用しないことを推奨する。
- MAP_GROWSDOWN
- スタック用に使用される。マッピングをメモリー内で逆向きに行うことを
カーネル仮想メモリーシステムに指示する。
(訳注:マッピングは通常はメモリーアドレスが増加する向きに行うが、
このオプションを指定すると逆向きにマッピングを行う)
-
MAP_HUGETLB (Linux 2.6.32 以降)
- "huge page"
を使ってマッピングを割り当てる。詳しい情報は、Linux
カーネルソースの
Documentation/vm/hugetlbpage.txt
を参照。
-
MAP_LOCKED (Linux 2.5.37 以降)
- マップされた領域のページを
mlock(2)
の方法でメモリー内にロックする。
それ以前のカーネルでは、このフラグは無視される。
-
MAP_NONBLOCK (Linux 2.5.46 以降)
-
MAP_POPULATE
と組み合わせた場合のみ意味を持つ。
read-ahead
(前もって読み込むこと)
を実行しない。
単に、すでに RAM
上に存在するページに対してのみページテーブルエントリーを作成する。
Linux 2.6.23
以降では、このフラグは
MAP_POPULATE
に何の影響も与えない。
いつか MAP_POPULATE と MAP_NONBLOCK
を組み合わせた場合の動作は実装し直されるかもしれない。
- MAP_NORESERVE
- このマッピングに対するスワップ空間の予約を行わない。
スワップ空間を予約した場合は、このマッピングの変更が必ず可能なことが
保証される。予約を行わなかった場合、物理メモリーに空きがないと
書き込み時に SIGSEGV
エラーを受け取ることがある。
proc(5) の /proc/sys/vm/overcommit_memory
ファイルについての議論も参照。
バージョン 2.6
より前のカーネルでは、このフラグは書き込み可能な
プライベートマッピングについてのみ効果があった。
-
MAP_POPULATE (Linux 2.5.46 以降)
- マッピング用のページテーブルを配置
(populate) する
ファイルマッピングの場合には、これによりファイルが先読み
(read-ahead)
が行われる。この以後は、マッピングに対するアクセスがページフォールトで
ブロックされることがなくなる。
Linux 2.6.23
以降でのみプライベートマッピングについて
MAP_POPULATE
がサポートされている。
-
MAP_STACK (Linux 2.6.27 以降)
- プロセスやスレッドのスタックに適したアドレスにマッピングを割り当てる。
現在のところ、このフラグは何もしないが、
glibc
のスレッド実装では使用されている。
これは、いくつかのアーキテクチャーではスタックの割り当てに関して特別な扱い
が必要な場合に、glibc
にそのサポートを後で透過的に実装できるようにする
ためである。
-
MAP_UNINITIALIZED (Linux 2.6.33
以降)
- 無名ページ (anonymous page)
のクリアを行わない。このフラグは組み込みデバイス
での性能向上を目的に作られてものである。カーネルの設定で
CONFIG_MMAP_ALLOW_UNINITIALIZED
オプションが有効になっている場合のみ、
このフラグは効果を持つ。
セキュリティ面の考慮から、このオプションは通常組み込みデバイス
(すなわち、
ユーザーメモリーの内容を完全に制御化におけるデバイス)
においてのみ有効にされる。
上記のフラグの中では、
MAP_FIXED だけが POSIX.1-2001
で規定されている。
しかしながら、ほとんどのシステムで
MAP_ANONYMOUS
(またはその同義語である
MAP_ANON)
もサポートされている。
いくつかのシステムでは、上記以外にフラグとして
MAP_AUTOGROW,
MAP_AUTORESRV,
MAP_COPY,
MAP_LOCAL
が規定されている。
mmap()
によってマップされたメモリーの属性は
fork(2)
の際に継承される。
ファイルはページサイズの整数倍の領域にマップされる。サイズがページサイズの
整数倍でないファイルの場合、マップ時に残りの領域は
0
で埋められ、この領域へ
書きこみを行ってもファイルに書き出されることはない。マッピングを行った元
ファイルのサイズを変更した場合、元ファイルの追加されたり削除された領域に対応
するマップされたページに対してどのような影響があるかは規定されていない。
システムコール
munmap()
は指定されたアドレス範囲のマップを消去し、
これ以降のその範囲内へのメモリー参照は不正となる。
この領域は、プロセスが終了したときにも自動的にアンマップされる。
一方、ファイルディスクリプターをクローズしても、この領域はアンマップされない。
addr
アドレスはページサイズの整数倍でなければならない。指定された範囲の一部分を
含む全てのページはアンマップされ、これ以降にこれらのページへの参照があると
SIGSEGV が発生する。
指定した範囲内にマップされたページが一つも含まれていない場合でも
エラーにならない。
ファイルと関連付けられたマッピングの場合、マッピングされたファイルの
st_atime フィールドは、
mmap()
されてからアンマップ
(unmap)
されるまでの間に更新されることがある。
それまでに更新が行われていなければ、マップされたページへの最初の参照があった
際に更新される。
PROT_WRITE と
MAP_SHARED
の両方を指定してマップされたファイルの場合、書き込みがあると、
st_ctime と
st_mtime
の両フィールドは、マップされた領域への書き込みより後で、
MS_SYNC または
MS_ASYNC
フラグを指定して
msync(2)
が呼ばれる前までに更新される。
mmap()
は成功するとマップされた領域へのポインターを返す。
失敗すると値
MAP_FAILED
(つまり
(void *) -1)
を返し、
errno
がエラーの内容にしたがってセットされる。
munmap() は成功すると 0
を返す。失敗すると -1
を返し、
errno
がセットされる
(多くの場合
EINVAL
になるだろう)。
- EACCES
- 以下のいずれかの場合。
ファイルディスクリプターの参照先が通常のファイルではない
(non-regular file) 。
ファイルマッピングを要求したが
fd
は読み込み用にオープンされていない。
MAP_SHARED を要求して
PROT_WRITE をセットしたが
fd は読み書きモード
( O_RDWR)
でオープンされていない、
PROT_WRITE
をセットしたが、ファイルは追加
(append) 専用である。
- EAGAIN
- ファイルがロックされている。またはロックされているメモリーが多すぎる
( setrlimit(2) を参照)。
- EBADF
-
fd
が有効なファイルディスクリプター
(file descriptor) ではない (かつ
MAP_ANONYMOUS
がセットされていない)。
- EINVAL
-
addr か length か offset
が適切でない
(例えば、大きすぎるとか、ページ境界にアラインメントされていない)。
- EINVAL
- (Linux 2.6.12 以降) length が 0
であった。
- EINVAL
-
flags に MAP_PRIVATE と
MAP_SHARED
のどちらも含まれていなかった、もしくは
その両方が含まれていた。
- ENFILE
- システム全体でオープンされているファイルの総数が上限に達した。
- ENODEV
- 指定されたファイルが置かれているファイルシステムがメモリーマッピングをサポート
していない。
- ENOMEM
- メモリーに空きがない、または処理中のプロセスのマッピング数が最大数を超過した。
- EPERM
-
prot 引き数は
PROT_EXEC
を行うように指定されているが、
no-exec
でマウントされたファイルシステム上のファイルに
マップ領域が対応している。
- EPERM
- 操作が file seal
により禁止されている。
fcntl(2) 参照。
- ETXTBSY
-
MAP_DENYWRITE
がセットされているが
fd
で指定されているオブジェクトは書き込み用に開かれている。
- EOVERFLOW
- 32
ビットアーキテクチャーで
large file
拡張を使っている場合
(つまり 64 ビットの off_t
を使う場合)、 length
で使うページ数と
offset
で使うページ数を足した値は
unsigned long (32 ビット)
を超えてしまう
(オーバーフローしてしまう)
場合がある。
マップ領域を利用する際に、以下のシグナルが発生することがある:
- SIGSEGV
- 読み込み専用で
mmap
された領域へ書き込みを行おうとした。
- SIGBUS
- バッファーのうち、ファイルに関連づけられていない部分
(例えばファイル末尾を越えた部分など。これには
他のプロセスがファイルを切り詰めた場合なども含まれる)
にアクセスしようとした。
SVr4, 4.4BSD, POSIX.1-2001.
mmap(),
msync(2) munmap()
が利用可能な POSIX
システムでは、
_POSIX_MAPPED_FILES は <unistd.h> で 0
より大きな値に定義される
(
sysconf(3) も参照のこと)。
(i386 などの)
いくつかのアーキテクチャーでは、
PROT_WRITE
をセットすると、暗黙のうちに
PROT_READ
がセットされる。
PROT_READ
をセットした際に暗黙のうちに
PROT_EXEC
がセットされるかどうかは、アーキテクチャー依存である。
移植性を考慮したプログラムでは、
新規にマップした領域でコードを実行したい場合は、常に
PROT_EXEC
をセットすべきである。
マッピングを作成する移植性のある方法は、
addr に 0 (NULL) を指定し、
flags から
MAP_FIXED
を外すことである。
この場合、システムがマッピング用のアドレスの選択を行う。
アドレスは既存のマッピングと衝突しないように、
かつ 0
にならないように選択される。
MAP_FIXED
フラグが指定され、かつ
addr が 0 (NULL)
の場合には、マップされるアドレスが
0 (NULL) になる。
特定の
flags 定数は
_BSD_SOURCE か
_SVID_SOURCE
のいずれかが定義された場合にのみ定義される。
(
_GNU_SOURCE
も定義されている必要がある。これらのフラグはすべて
Linux 固有のものなので、
特に
_GNU_SOURCE
を必要とする点はもっと論理的に決められるべきであった。)
関係するフラグは
MAP_32BIT,
MAP_ANONYMOUS
(とその同義語の
MAP_ANON),
MAP_DENYWRITE,
MAP_EXECUTABLE,
MAP_FILE,
MAP_GROWSDOWN,
MAP_HUGETLB,
MAP_LOCKED,
MAP_NONBLOCK,
MAP_NORESERVE,
MAP_POPULATE,
MAP_STACK である。
このページでは glibc の
mmap()
のラッパー関数が提供するインターフェースに
ついて説明している。元々は、この関数は同じ名前のシステムコールを起動していた。
カーネル 2.4
以降、このシステムコールは
mmap2(2)
に取って代わられ、現在
では、 glibc の
mmap()
のラッパー関数は
offset
を適切に調整してから
mmap2(2) を起動する。
Linux においては、上記の
MAP_NORESERVE
で述べられているような保証はない。
デフォルトでは、システムがメモリーを使い切った場合には、
どのプロセスがいつ強制終了されるか分からないからである。
2.6.7
より前のカーネルでは、
prot に
PROT_NONE
が指定された場合にのみ、
MAP_POPULATE
フラグが効力を持つ。
SUSv3 では、
length が 0
の場合、
mmap()
は失敗すると規定されている。しかしながら、2.6.12
より前のカーネルでは、
この場合に
mmap()
は成功していた
(マッピングは作成されず、
addr が返されていた)。
カーネル 2.6.12
以降では、
mmap()
はエラー
EINVAL
で失敗する。
POSIX では、
システムはオブジェクト末尾の部分ページを常に
0 で埋め、
末尾より後ろのオブジェクトを決して変更してはならない、と規定している。
Linux では、
オブジェクト末尾より後ろの部分ページにデータを書き込んだ場合、
そのファイルをクローズしてアンマップした後であってもページキャッシュにデータが残り続け、
データがファイル自体に書き込まれていなくても、
それ以降のマッピングで変更された内容が見える可能性がある。
いくつかの場合では、
アンマップを行う前に
msync(2)
を呼び出すことで、
この状況を修正することができる。
しかし、 これは tmpfs
では機能しない
(例えば、
shm_overview(7)
で説明されている POSIX
共有メモリーインターフェースを使った場合)。
以下のプログラムは、一番目のコマンドライン引き数で指定された
ファイルの一部を標準出力に表示する。
表示する範囲は、二番目、三番目のコマンドライン引き数で渡される
オフセットと長さで指定される。
このプログラムは、指定されたファイルの必要なページのメモリー
マッピングを作成し、
write(2)
を使って所望のバイトを出力する。
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Can't display bytes past end of file */
} else { /* No length arg ==> display to end of file */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
getpagesize(2),
memfd_create(2),
mincore(2),
mlock(2),
mmap2(2),
mprotect(2),
mremap(2),
msync(2),
remap_file_pages(2),
setrlimit(2),
shmat(2),
shm_open(3),
shm_overview(7)
proc(5) の
/proc/[pid]/maps,
/proc/[pid]/map_files,
/proc/[pid]/smaps の説明。
B.O. Gallmeister, POSIX.4, O'Reilly, pp. 128-129 and 389-391.
この man ページは Linux
man-pages
プロジェクトのリリース
3.79 の一部
である。プロジェクトの説明とバグ報告に関する情報は
http://www.kernel.org/doc/man-pages/
に書かれている。