mmapというファイルやデバイスをメモリーにマップするシステムコールの解説
選択3: mmap
mmapについて説明しなさい。その際に必ずanonymous メモリーについて言及すること。
概要
mmapとは, ファイルやデバイスをメモリーにマップするシステムコールです.
一応mmapはPOSIXに含まれているシステムコールです.
しかし,
POSIXに定義されている動作はごく一部で,
特にflags
引数に指定できるのはMAP_FIXED
, MAP_PRIVATE
, MAP_SHARED
の3つしかなく,
無名ページもサポートしていません.
それぞれのUNIXが独自に拡張しており,
ゆるい共通APIを持っていますが厳格な規格にはなっていないようです.
mmapの引数
mmap
の引数を軽く説明します.
void* addr
: カーネルがメモリのどの位置にアドレスを配置するかヒントを設定できる. 通常NULL
で動かすsize_t length
: 確保するページのサイズint prot
: ページが実行可能, 書き込み可能, 読み込み可能かを論理和でモード指定するint flags
: 色々な設定を論理和で行うint fd
: ファイルディスクリプタoff_t offset
: ファイルをどの部分から読み込むかのオフセット
flagsの意味のある非推奨ではないフラグ
flags
には互換性のために現在は使われなくなったり,
非推奨になったフラグがとても多いため,
Manに書かれているもので非推奨ではない,
現在使う意義のあるものだけを抽出してみます.
MAP_SHARED
: マッピングを共有して, 更新がファイルをマッピングしている他のプロセスに見えるようになる. マップ元のファイルが変更されるとマップ領域も更新されるMAP_PRIVATE
: マッピングをプライベートにして, 同じファイルをマッピングしている他のプロセスに更新が見えなくなる. マップ元のファイルが変更されてもマップ領域が変更されるかは規定されていないMAP_ANONYMOUS
: マッピングがファイルと関連付けされなくなるMAP_GROWSDOWN
: マッピングをメモリ上で逆順に行うMAP_HUGETLB
: 大きいページサイズを使用するMAP_LOCKED
: メモリがスワップ領域にページングされるのを防ぐMAP_NORESERVE
: スワップ空間の予約を行わない. これを選択した場合, 書き込み時に物理メモリに空きがないとSIGSEGV
を受け取ることがあるMAP_POPULATE
: ファイルマッピングの場合ファイルが先読みされるので, アクセス時にページフォールトしなくなるMAP_UNINITIALIZED
: 無名ページのクリアを行わない. カーネルの設定で有効になっていないと効果を発揮しない. セキュリティを犠牲にパフォーマンスが向上する
ファイルマッピングと無名マッピング
引数flags
にMAP_ANONYMOUS
を指定せずに,
引数fd
にファイルオープン関数open
の返り値を渡した場合,
mmapはファイルマッピングモードとなり,
SHAREDモードの場合マッピングされた領域に書き込みされた場合ファイルを更新します.
引数offset
はファイルを何処から読み込むというオフセットに使われます.
引数flags
にMAP_ANONYMOUS
を論理和で指定した場合,
mmapはファイルを使わずに無名マッピングモードになり,
Linuxの場合fd
とoffset
は無視されます.
しかし,
他の実装はMAP_ANONYMOUS
を使う場合fd
を-1
にすることを要求する可能性があるため,
移植性のためfd
には-1
を指定するべきでしょう.
offset
はどうするべきか書いてなかったのですが多分0
で良いでしょう.
この無名マッピングモードでマップされたメモリ領域をanonymous メモリーと呼ぶことがあります.
mmapを使ってabcと書かれているファイルのbをdに変えるサンプルコード
manにかかれていたサンプルコードはエラー処理などが長かったので簡単なサンプルコードを書きました.
C99です.
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
int main() {
int fd = open("abc.txt", O_RDWR);
struct stat stat_buf;
fstat(fd, &stat_buf);
char* addr = mmap(NULL, stat_buf.st_size, PROT_WRITE, MAP_SHARED, fd, 0);
addr[1] = 'd';
return 0;
}
mallocはどのようにmmapを使っているか
mallocの著名な実装は, メモリ領域を確保するためにmmapの無名マッピングモードを使っています.
glibc-2.26の/malloc/malloc.c
を見てみました.
以下のマクロが定義されて使われていました.
#define MMAP(addr, size, prot, flags) \
__mmap((addr), (size), (prot), (flags)|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)
forkした後, 他のプロセスがページ内容を変更した時に, それぞれのメモリ領域を独立させる必要があるので, 匿名目マッピングモードかつPRIVATEモードになっています.
msync
msyncはファイルをマップしたメモリーと同期させるシステムコールです.
これも一応POSIXにあります.
mmapでマップされたファイルの更新は非同期に行われることがあるので, これを使えば同期的に行うことが可能です.
munmap
mmapでのマップを解除します.
これも一応POSIXにあります.
プロセスが終了した時に自動的にアンマップされるので, メモリ管理を提供したりするプログラムを書く場合以外は気にしなくても良さそうですね.
解除された領域にアクセスした場合SIGSEGV
が発生します.