というわけで、まずは/usr/src/servers/vm/mmap.cにあるmmapの動作をみてます。
mmapの本体はかなり小さめ。linuxのsys_mmap()も小さいけど中で呼んでるdo_mmap_pgoff()がメインの処理っぽいとも言えると思うけど、minixはほぼdo_mmapで完結してるもよう。
/*===========================================================================* * do_mmap * *===========================================================================*/ PUBLIC int do_mmap(message *m) { int r, n; struct vmproc *vmp; int mfflags = 0; struct vir_region *vr = NULL; if((r=vm_isokendpt(m->m_source, &n)) != OK) { vm_panic("do_mmap: message from strange source", m->m_source); } vmp = &vmproc[n]; if(m->VMM_FLAGS & MAP_LOWER16M) printf("VM: warning for %d: MAP_LOWER16M not implemented\n", m->m_source); if(!(vmp->vm_flags & VMF_HASPT)) return ENXIO; if(m->VMM_FD == -1 || (m->VMM_FLAGS & MAP_ANON)) { int s; vir_bytes v; u32_t vrflags = VR_ANON | VR_WRITABLE; size_t len = (vir_bytes) m->VMM_LEN; if(m->VMM_FD != -1) { return EINVAL; } if(m->VMM_FLAGS & MAP_CONTIG) mfflags |= MF_CONTIG; if(m->VMM_FLAGS & MAP_PREALLOC) mfflags |= MF_PREALLOC; if(m->VMM_FLAGS & MAP_ALIGN64K) vrflags |= VR_PHYS64K; if(len % VM_PAGE_SIZE) len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE); if(!(vr = map_page_region(vmp, arch_vir2map(vmp, vmp->vm_stacktop), VM_DATATOP, len, MAP_NONE, vrflags, mfflags))) { return ENOMEM; } } else { return ENOSYS; } /* Return mapping, as seen from process. */ vm_assert(vr); m->VMM_RETADDR = arch_map2vir(vmp, vr->vaddr); return OK; }
この関数を見てて気づいたのは、"m->VMM_FD == -1"が2ヶ所出現して、そのうちの1個はEINVALを返すパターンになってるということと、
↓のif文の条件に該当しないとENOSYSを返しているってところ。
if(m->VMM_FD == -1 || (m->VMM_FLAGS & MAP_ANON)) { 中略 } else { return ENOSYS; }
ここから察するにminix3.1.4のmmapは・・・
1.アノニマスmmapのみサポートしている
2.MAP_ANONを使う場合はfdは-1にしないといけない
ということっぽい。
そんな分けで、ちょっとしたテストを。。。
#munmap()呼んでないけど、コンパイル時にmunmapが定義されていないとかエラーが出たので呼んでません。
#テストプログラムだし別に良いやってことでwww
#include <stdio.h> #include <sys/mman.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> static void print_result(char *name, int ret) { printf("test %s was %s(errno is %d)\n", name, (ret == 0) ? "succeed" : "failed", ret); } static int do_mmap_test(int fd, int flg) { void *p; errno = 0; p = mmap(NULL, 1024, PROT_READ | PROT_WRITE, flg, fd, 0); return errno; } static void test4(void) { int ret; int fd; printf("mmap test4 fd = /dev/null and MAP_PRIVATE\n"); fd = open("/dev/null", O_RDWR); if (fd == -1) { printf("open error\n"); return; } ret = do_mmap_test(fd, MAP_PRIVATE); print_result("test4", ret); close(fd); } static void test3(void) { int ret; int fd; printf("mmap test3: fd = /dev/null and MAP_ANON\n"); fd = open("/dev/null", O_RDWR); if (fd == -1) { printf("open error\n"); return; } ret = do_mmap_test(fd, MAP_ANON); print_result("test3", ret); close(fd); } static void test2(void) { int ret; printf("mmap test2: fd = -1 and MAP_PRIVATE\n"); ret = do_mmap_test(-1, MAP_PRIVATE); print_result("test2", ret); } static void test1(void) { int ret; printf("mmap test1: fd = -1 and MAP_ANON\n"); ret = do_mmap_test(-1, MAP_ANON); print_result("test1", ret); } int main(int argc, char **argv) { test1(); test2(); test3(); test4(); return 0; }
これをコンパイルして実行すると、こんな感じに。
[masami@minix:/home/masami]$ ./a.out mmap test1: fd = -1 and MAP_ANON test test1 was succeed(errno is 0) mmap test2: fd = -1 and MAP_PRIVATE test test2 was succeed(errno is 0) mmap test3: fd = /dev/null and MAP_ANON test test3 was failed(errno is 22) mmap test4 fd = /dev/null and MAP_PRIVATE test test4 was failed(errno is 38)
結局、fdに-1を渡せばアノニマスmmapできるけど、fdが何かのファイルディスクリプタを指しているとmmapは失敗する。
この辺の動きをLinuxと合わせるのはおもしろそうなので、時間を見つつハックしよう(*゚▽゚)ノ
追記:
メモリの割り当てはmap_page_region()が主要な処理をになっているもよう。