浅析 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.csdn.net/crazyjixiang/article/details/662699


所谓虚拟进程地址空间是虚拟的,这并不是废话,每个进程都有自己的私有用户空间,这个空间对系统中的其他进程是不可见的,最高的1G内核空间则由所有进程以及内核所共享.另外,进程的"用户空间" 也叫 "地址空间" ,用户空间是独立的,每个进程都有属于自己的最大3G的用户空间,一个进程对其中某个地址的访问,与其它进程对同一地址的访问绝不冲突. 以前OS只有物理内存的概念.

浅析 mmap (内存映射)浅析 mmap (内存映射)

继续看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:映射区的长度 ( 按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;
}


这里代码很清晰,最后使用一个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.csdn.net/crazyjixiang/article/details/6626995





原文链接: http://blog.csdn.net/crazyjixiang/article/details/6645739

你可能感兴趣的:(浅析 mmap (内存映射))