MMAP(2) | Linux Programmer's Manual | MMAP(2) |
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) をとったものを 指定できる。
flags 引き数により、マッピングに対する更新が同じ領域をマッピングしている 他のプロセスに見えるか、更新がマッピング元のファイルを通じて 伝えられるか、が決定される。この動作は、以下の値のいずれか一つだけ (複数は指定できない) を flags に含めることで指定する。
上記の二つのフラグは POSIX.1-2001 で規定されている。
さらに、以下の値のうち 0 個以上をビット毎の論理和 (OR) で flags に指定することができる。
上記のフラグの中では、 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 になるだろう)。
マップ領域を利用する際に、以下のシグナルが発生することがある:
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/ に書かれている。
2015-01-22 | Linux |