ゼロ番地を先頭とするページにマップする
mmapのflags引数にMAP_FIXEDフラグを指定することで、OSの実装によってはゼロ番地を先頭とするページにマップできます*1
たとえば、i386以降のCPU, Vine Linux 4.0, gcc-4.1.1を使った環境で以下のコードをコンパイルして実行するとゼロ番地にアクセスできます。
#include <sys/mman.h> #include <unistd.h> #include <stdio.h> int main(){ int* p = (int*)mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0); *p = 1; printf("*(int*)%d = %d\n", (int)p, *(int*)0); return 0; }
実行すると以下のようになります。
$ gcc main.c $ ./a.out *(int*)0 = 1 $
以下、注意点。
Cの仕様上では、start引数に0を指定しても数値の0を指すとは限りません。なぜなら、start引数の型はvoid*であり、Cの仕様ではポインタを書くべきところに書かれた0は、コンパイル時にヌル・ポインタに変換されることが保証されているからです*2(コンパイラがポインタ型の式だと判断できる場合のみ)。
なお、C++でもほとんど同様のようです*3。自分には、CとC++の違いは、C言語の場合、#define NULL (void*)0 か #define NULL 0 どちらでもありうるが、C++だと #define NULL (void*)0 がありえないということしかわかりませんでした。
よって、ハードウェア, OS, C/C++のコンパイラによっては、start引数で渡す値がコンパイル時に0ではない値に変換されているかもしれないので、ソースコードでstart引数に0を指定したからといって、実行時にmmapでマップされたページが必ずしもゼロ番地ではないかも知れないことに注意です。
以上、何の役に立つかわからないけど面白かったのでメモっときます。
ちなみにWindowsの場合、ゼロ番地から64KB分*4のアドレスをユーザプログラムのプログラマがVirtualAlloc APIで確保(予約)することはできません。これは、ヌル・ポインタが指すアドレス先に値を代入しようとしたときに、必ずメモリアクセス違反が発生するようにするために、意図的にそう決まっています*5
ということは、Windowsが動くような環境において、ヌル・ポインタが0と一致する(させる)のは当然だとMicrosoftは考えているのでしょうね。
*1:SUSv3では、「MAP_FIXEDフラグを指定した場合、start引数はマップされるページのヒントとなるアドレスではなく、マップさせたいページのアドレスを意味するようになり、マップに失敗した場合はエラーを返す」としか書いてありません。よって、startに0を指定した場合は必ず失敗するようにmmapを実装しても仕様を満たします。
*2:http://www.kouno.jp/home/c_faq/c5.html 5.2項
*3:注解 C++ リファレンスマニュアル 4.6: ISBN 4901280392 といっても、これ古い仕様みたいですけど・・・
*4:Windows2000の場合。Windows98だと4KB分