mmap() creates a new mapping in the virtual address space of the call‐ing process.
这是来自linux man pages的一段解释.
这里边有个virtual address space of the call-ing process的概念.
之前在这篇文章提到过:http://blog.csdn.net/crazyjixiang/article/details/662699
所谓虚拟进程地址空间是虚拟的,这并不是废话,每个进程都有自己的私有用户空间,这个空间对系统中的其他进程是不可见的,最高的1G内核空间则由所有进程以及内核所共享.另外,进程的"用户空间" 也叫 "地址空间" ,用户空间是独立的,每个进程都有属于自己的最大3G的用户空间,一个进程对其中某个地址的访问,与其它进程对同一地址的访问绝不冲突. 以前OS只有物理内存的概念.
继续看mmap,先看下mmap的原型:
SYNOPSIS #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
length:映射区的长度 ( 按block计算 4K)
prot:映射区的内存保护标志(PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE(页不可以被访问))
flags:指定映射对象的类型,映射选项和映射页是否可以共享
fd:文件描述符
offset:偏移量
mmap通俗的说将文件映射到虚拟进程地址空间,为什么要这么做?想想平时是怎么打开? 平时应该是open一个文件,然后进行一系列的系统调用 进行读写.这种操作效率是很低的,关于IO的操作实在太多,内核态和用户态要不停的切换. 而mmap映射到虚拟进程地址空间后,可以直接使用memcpy这些对对文件进行操作,也就是说 只要进行一次系统调用,open后把fd传给mmap即可.
下面先看个例子:
#include <stdio.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd = open(argv[1], O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); int fLen = lseek(fd, 1, SEEK_END); char* mm = mmap(0, fLen, PROT_READ, MAP_PRIVATE, fd, 0); fprintf(stdout, "%s\n", mm); getchar(); return 0; }
[crazybaby@localhost ~]$ ps -ef | grep a.out root 5161 5021 0 19:17 pts/0 00:00:00 ./a.out main.c 500 5136 3991 0 19:17 pts/1 00:00:00 grep --color=auto a.out
[root@localhost 5161]# ls attr cpuset latency mountstats root statm auxv cwd limits net sched status cgroup environ loginuid numa_maps schedstat syscall clear_refs exe maps oom_adj sessionid task cmdline fd mem oom_score smaps wchan comm fdinfo mountinfo pagemap stack coredump_filter io mounts personality stat
[root@localhost 5161]# cat maps 00400000-00401000 r-xp 00000000 08:05 523430 /home/crazybaby/MyProjects/welcomeC/a.out 00600000-00601000 rw-p 00000000 08:05 523430 /home/crazybaby/MyProjects/welcomeC/a.out 30af000000-30af020000 r-xp 00000000 08:02 191241 /lib64/ld-2.12.90.so 30af21f000-30af220000 r--p 0001f000 08:02 191241 /lib64/ld-2.12.90.so 30af220000-30af221000 rw-p 00020000 08:02 191241 /lib64/ld-2.12.90.so 30af221000-30af222000 rw-p 00000000 00:00 0 30af400000-30af599000 r-xp 00000000 08:02 191242 /lib64/libc-2.12.90.so 30af599000-30af799000 ---p 00199000 08:02 191242 /lib64/libc-2.12.90.so 30af799000-30af79d000 r--p 00199000 08:02 191242 /lib64/libc-2.12.90.so 30af79d000-30af79e000 rw-p 0019d000 08:02 191242 /lib64/libc-2.12.90.so 30af79e000-30af7a4000 rw-p 00000000 00:00 0 7fdef3041000-7fdef3044000 rw-p 00000000 00:00 0 7fdef3051000-7fdef3053000 rw-p 00000000 00:00 0 7fdef3053000-7fdef3054000 r--p 00000000 08:05 523421 /home/crazybaby/MyProjects/welcomeC/test 7fdef3054000-7fdef3055000 rw-p 00000000 00:00 0 7fffa2fde000-7fffa2fff000 rw-p 00000000 00:00 0 [stack] 7fffa2fff000-7fffa3000000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
该文件有6列.
拿第一行为例:
00400000-00401000 虚拟地址空间
r-xp 玩linux的应该很熟悉了,权限
00000000 这是映射的文件在地址空间的偏移量
08:05 文件的主设备号和次设备号
523430 文件的节点号
/home/crazybaby/MyProjects/welcomeC/a.out 这个明显是我们程序的路径了
虚拟地址:
00400000-00401000 这表示程序的正文段虚拟地址.
00600000-00601000 表示程序的数据段
glibc 是linux下c的运行库,所以大部分的程序都会调用.
ld 是动态链接器,用于查找动态链接库
下面是
程序的正文段
栈
最后有两个 vdso 和 vsyscall
我们来看下这两个是什么?
ldd任意文件.
linux-vdso.so.1 => (0x00007fff7bbff000) libc.so.6 => /lib64/libc.so.6 (0x00000030af400000) /lib64/ld-linux-x86-64.so.2 (0x00000030af000000)这里linux-vdso.so.1是个虚拟库 ,一般称为Virtual Dynamic Shared Object,它只存在于程序的地址空间中,该虚拟库为用户程序以处理器可支持的最快的方式访问系统函数提供了必要的逻辑.
我们继续,再做这样一个实验,测试程序改成:
int main(int argc, char *argv[]) { int fd = open(argv[1], O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); int fLen = lseek(fd, 1, SEEK_END); //char* mm = mmap(0, fLen, PROT_READ, MAP_PRIVATE, fd, 0); //fprintf(stdout, "%d->%s\n", fLen,mm); getchar(); return 0; }
000400000-00401000 r-xp 00000000 08:05 523422 /home/crazybaby/MyProjects/welcomeC/a.out 00600000-00601000 rw-p 00000000 08:05 523422 /home/crazybaby/MyProjects/welcomeC/a.out 30af000000-30af020000 r-xp 00000000 08:02 191241 /lib64/ld-2.12.90.so 30af21f000-30af220000 r--p 0001f000 08:02 191241 /lib64/ld-2.12.90.so 30af220000-30af221000 rw-p 00020000 08:02 191241 /lib64/ld-2.12.90.so 30af221000-30af222000 rw-p 00000000 00:00 0 30af400000-30af599000 r-xp 00000000 08:02 191242 /lib64/libc-2.12.90.so 30af599000-30af799000 ---p 00199000 08:02 191242 /lib64/libc-2.12.90.so 30af799000-30af79d000 r--p 00199000 08:02 191242 /lib64/libc-2.12.90.so 30af79d000-30af79e000 rw-p 0019d000 08:02 191242 /lib64/libc-2.12.90.so 30af79e000-30af7a4000 rw-p 00000000 00:00 0 7f9c1ea23000-7f9c1ea26000 rw-p 00000000 00:00 0 7f9c1ea35000-7f9c1ea37000 rw-p 00000000 00:00 0 7fff1b65c000-7fff1b67d000 rw-p 00000000 00:00 0 [stack] 7fff1b7e0000-7fff1b7e1000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] ~
缺一行这个:
7fdef3053000-7fdef3054000 r--p 00000000 08:05 523421 /home/crazybaby/MyProjects/welcomeC/test
这个权限只有读和私有.
Tips:
你可以通过pmap命令直接来读取程序运行的进程的文件映射情况
pmap [pid]
查看真实内存使用
pmap -x [pid]
这行就是我们已经映射的文件了.前面就是虚拟地址空间,至于虚拟地址空间和物理空间的转换 ,请参考我另外一篇blog:
http://blog.csdn.net/crazyjixiang/article/details/6626995