MMAP
Section: Linux Programmer's Manual (2)
Updated: 2007-07-10
Index
JM Home Page
roff page
名前
mmap, munmap - ファイルやデバイスをメモリにマップ/アンマップする
書式
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *start, size_t length);
説明
mmap()
は、新しいマッピングを呼び出し元プロセスの仮想アドレス空間に作成する。
新しいマッピングの開始アドレスは
start
で指定される。マッピングの長さは
length
引き数で指定される。
start
が NULL の場合、カーネルがマッピングを作成するアドレスを選択する。
この方法は最も移植性のある新しいマッピングの作成方法である。
start
が NULL でない場合、カーネルはマッピングをどこに配置するかのヒントとして
start
を使用する。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(2)
が呼ばれるまで行われないこともある。
- MAP_PRIVATE
-
プライベートな copy-on-write (書き込み時コピー) マップを生成する。
マッピングに対する更新は同じファイルをマッピングしている他のプロセス
には見えず、更新がマッピング元のファイルを通じて伝えられることもない。
mmap()
の呼び出し後にマッピング元のファイルに対して行われた変更が、
マップ領域に反映されるかどうかは規定されていない。
上記の二つのフラグは POSIX.1-2001 で規定されている。
さらに、以下の値のうち 0 個以上をビット毎の論理和 (OR) で
flags
に指定することができる。
- MAP_32BIT
-
マッピングをプロセスのアドレス空間の先頭 2GB の範囲に配置する。
MAP_FIXED
がセットされている場合は、このフラグは無視される。
現在のところ、このフラグがサポートされているのは、x86-64 アーキテクチャ上
の 64 ビット・プログラムについてのみである。
- 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
-
start
をアドレスのヒントとして使用するのではなく、
start
で指定されたアドレスをそのまま使用してマッピングを配置する。
start
はページサイズの倍数でなければならない。
start
と
len
で指定されたメモリ領域が既存のマッピングのページと重なる場合、
既存のマッピングの重なった部分は捨てられる。
もし指定されたアドレスが使用できない場合、
mmap()
は失敗する。
マッピングに対して固定アドレスを要求するのは移植性の面で劣るので、
このオプションは使用しないことを推奨する。
- MAP_GROWSDOWN
-
スタック用に使用される。マッピングをメモリ内で逆向きに行うことを
カーネル仮想メモリシステムに指示する。
(訳注:マッピングは通常はメモリ・アドレスが増加する向きに行うが、
このオプションを指定すると逆向きにマッピングを行う)
- MAP_LOCKED (Linux 2.5.37 以降)
-
マップされた領域のページを
mlock(2)
の方法でメモリ内にロックする。
それ以前のカーネルでは、このフラグは無視される。
- MAP_NONBLOCK (Linux 2.5.46 以降)
-
MAP_POPULATE
と組み合わせた場合のみ意味を持つ。
read-ahead (前もって読み込むこと) を実行しない。
単に、すでに RAM 上に存在するページに対して
ページテーブルエントリを作成するだけである。
- MAP_NORESERVE
-
このマッピングに対するスワップ空間の予約を行わない。
スワップ空間を予約した場合は、このマッピングの変更が必ず可能なことが
保証される。予約を行わなかった場合、物理メモリに空きがないと
書き込み時に
SIGSEGV
エラーを受け取ることがある。
proc(5)
の
/proc/sys/vm/overcommit_memory
ファイルについての議論も参照。
バージョン 2.6 より前のカーネルでは、このフラグは書き込み可能な
プライベート・マッピングについてのみ効果があった。
- MAP_POPULATE (Linux 2.5.46 以降)
-
ファイルを前もって読み込む (read-ahead) ことで、
ファイルマッピング用のページテーブルを配置 (populate) する
(前もってページフォールトさせる (prefault))。
この後は、マッピングに対するアクセスがページフォールトで
ブロックされなくなる。
上記のフラグの中では、
MAP_FIXED
だけが POSIX.1-2001 で規定されている。
しかしながら、ほとんどのシステムで
MAP_ANONYMOUS
(またはその同義語である
MAP_ANON)
もサポートされている。
いくつかのシステムでは、上記以外にフラグとして MAP_AUTOGROW, MAP_AUTORESRV,
MAP_COPY, MAP_LOCAL が規定されている。
mmap()
によってマップされたメモリの属性は
fork(2)
の際に継承される。
ファイルはページサイズの整数倍の領域にマップされる。サイズがページサイズの
整数倍でないファイルの場合、マップ時に残りの領域は 0 で埋められ、この領域へ
書きこみを行ってもファイルに書き出されることはない。マッピングを行った元
ファイルのサイズを変更した場合、元ファイルの追加されたり削除された領域に対応
するマップされたページに対してどのような影響があるかは規定されていない。
システムコール
munmap()
は指定されたアドレス範囲のマップを消去し、
これ以降のその範囲内へのメモリ参照は不正となる。
この領域は、プロセスが終了したときにも自動的にアンマップされる。
一方、ファイル記述子をクローズしても、この領域はアンマップされない。
start
アドレスはページサイズの整数倍でなければならない。指定された範囲の一部分を
含む全てのページはアンマップされ、これ以降にこれらのページへの参照があると
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) 。
MAP_PRIVATE
を要求したが
fd
は読み込み用にオープンされていない。
MAP_SHARED
を要求して
PROT_WRITE
をセットしたが
fd
は読み書きモード
(O_RDWR)
でオープンされていない、
PROT_WRITE
をセットしたが、ファイルは追加 (append) 専用である。
- EAGAIN
-
ファイルがロックされている。またはロックされているメモリが多すぎる
(setrlimit(2)
を参照)。
- EBADF
-
fd
が有効なファイル記述子 (file descriptor) ではない
(かつ
MAP_ANONYMOUS
がセットされていない)。
- EINVAL
-
start
か
length
か
offset
が適切でない
(例えば、大きすぎるとか、ページ境界にアラインメントされていない)。
- EINVAL
-
(Linux 2.6.12 以降)
length
が 0 であった。
- EINVAL
-
flags
に
MAP_PRIVATE
と
MAP_SHARED
のどちらも含まれていなかった、もしくは
その両方が含まれていた。
- ENFILE
-
オープンされたファイルの総数がシステムの制限に達した。
- ENODEV
-
指定されたファイルが置かれているファイルシステムがメモリマッピングをサポート
していない。
- ENOMEM
-
メモリに空きがない、または処理中のプロセスのマッピング数が最大数を超過した。
- EPERM
-
prot
引き数は
PROT_EXEC
を行うように指定されているが、
no-exec でマウントされたファイルシステム上のファイルに
マップ領域が対応している。
- ETXTBSY
-
MAP_DENYWRITE
がセットされているが
fd
で指定されているオブジェクトは書き込み用に開かれている。
マップ領域を利用する際に、以下のシグナルが発生することがある:
- SIGSEGV
-
読み込み専用で mmap された領域へ書き込みを行おうとした。
- SIGBUS
-
バッファのうち、ファイルに関連づけられていない部分
(例えばファイル末尾を越えた部分など。これには
他のプロセスがファイルを切り詰めた場合なども含まれる)
にアクセスしようとした。
準拠
SVr4, 4.4BSD, POSIX.1-2001.
可用性
mmap(),
msync(2)
munmap()
が利用可能な POSIX システムでは、
_POSIX_MAPPED_FILES
は <unistd.h> で 0 より大きな値に定義される
(sysconf(3)
も参照のこと)。
注意
カーネル 2.4 以降、このシステムコールは
mmap2(2)
に取って代わられた。
現在では、
glibc の
mmap()
のラッパー関数は
offset
を適切に調整してから
mmap2(2)
を起動する。
(x86 などの) いくつかのアーキテクチャでは、
PROT_WRITE
をセットすると、暗黙のうちに
PROT_READ
がセットされる。
PROT_READ
をセットした際に暗黙のうちに
PROT_EXEC
がセットされるかどうかは、アーキテクチャ依存である。
移植性を考慮したプログラムでは、
新規にマップした領域でコードを実行したい場合は、常に
PROT_EXEC
をセットすべきである。
バグ
Linux においては、上記の
MAP_NORESERVE
で述べられているような保証はない。
デフォルトでは、システムがメモリを使い切った場合には、
どのプロセスがいつ強制終了されるか分からないからである。
2.6.7 より前のカーネルでは、
prot
に
PROT_NONE
が指定された場合にのみ、
MAP_POPULATE
フラグが効力を持つ。
SUSv3 では、
length
が 0 の場合、
mmap()
は失敗すると規定されている。しかしながら、2.6.12 より前のカーネルでは、
この場合に
mmap()
は成功していた (マッピングは作成されず、
start
が返されていた)。
カーネル 2.6.12 以降では、
mmap()
はエラー
EINVAL
で失敗する。
例
以下のプログラムは、一番目のコマンドライン引き数で指定された
ファイルの一部を標準出力に表示する。
表示する範囲は、二番目、三番目のコマンドライン引き数で渡される
オフセットと長さで指定される。
このプログラムは、指定されたファイルの必要なページのメモリ・
マッピングを作成し、
write(2)
を使って所望のバイトを出力する。
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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) == 0) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
if (fstat(fd, &sb) == -1) { /* To obtain file size */
perror("fstat");
exit(EXIT_FAILURE);
}
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) {
perror("mmap");
exit(EXIT_FAILURE);
}
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
perror("write");
else
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
関連項目
getpagesize(2),
mincore(2),
mlock(2),
mmap2(2),
mremap(2),
msync(2),
remap_file_pages(2),
setrlimit(2),
shm_open(3)
B.O. Gallmeister, POSIX.4, O'Reilly, pp. 128-129 and 389-391.
Index
- 名前
-
- 書式
-
- 説明
-
- 返り値
-
- エラー
-
- 準拠
-
- 可用性
-
- 注意
-
- バグ
-
- 例
-
- 関連項目
-
This document was created by
man2html,
using the manual pages.
Time: 04:31:53 GMT, November 19, 2007