深析 mmap (内存映射)

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.youkuaiyun.com/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);
addr:映射区的地址

length:映射区的长度

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; }

这里代码很清晰,最后使用一个getchar() 防止进程终止, 运行后,一直处于等待状态,所谓Everything is a file. 进程也是文件. 我们进/proc/pid 来查看我们的程序运行的进程文件.

[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

pid为5161.

[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

这是我们进程下的创建的文件,可见有提供给我们有很多信息,这次我们只要关系 maps这个文件即可.

[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; }

再看下maps:

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.youkuaiyun.com/crazyjixiang/article/details/6626995




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值