内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改能够直接反映到内核空间,一样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间用户空间二者之间须要大量数据传输等操做的话效率是很是高的。linux
如下是一个把广泛文件映射到用户空间的内存区域的示意图。
图一:
2、基本函数
mmap函数是unix/linux下的系统调用,详细内容可参考《Unix Netword programming》卷二12.2节。
mmap系统调用并非彻底为了用于共享内存而设计的。它自己提供了不一样于通常对普通文件的访问方式,进程能够像读写内存同样对普通文件的操做。而Posix或系统V的共享内存IPC则纯粹用于共享目的,固然mmap()实现共享内存也是其主要应用之一。
mmap系统调用使得进程之间经过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程能够像访问普通内存同样对文件进行访问,没必要再调用read(),write()等操做。mmap并不分配空间, 只是将文件映射到调用进程的地址空间里(可是会占掉你的 virutal memory), 而后你就能够用memcpy等操做写文件, 而不用write()了.写完后,内存中的内容并不会当即更新到文件中,而是有一段时间的延迟,你能够调用msync()来显式同步一下, 这样你所写的内容就能当即保存到文件里了.这点应该和驱动相关。 不过经过mmap来写文件这种方式没办法增长文件的长度, 由于要映射的长度在调用mmap()的时候就决定了.若是想取消内存映射,能够调用munmap()来取消内存映射
void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
mmap用于把文件映射到内存空间中,简单说mmap就是把一个文件的内容在内存里面作一个映像。映射成功后,用户对这段内存区域的修改能够直接反映到内核空间,一样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间用户空间二者之间须要大量数据传输等操做的话效率是很是高的。
原理
首先,“映射”这个词,就和数学课上说的“一一映射”是一个意思,就是创建一种一一对应关系,在这里主要是只 硬盘上文件 的位置与进程 逻辑地址空间 中一块大小相同的区域之间的一一对应,如图1中过程1所示。这种对应关系纯属是逻辑上的概念,物理上是不存在的,缘由是进程的逻辑地址空间自己就是不存在的。在内存映射的过程当中,并无实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是创建并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,因此创建内存映射的效率很高。数据结构
图1.内存映射原理 函数
既然创建内存映射没有进行实际的数据拷贝,那么进程又怎么能最终直接经过内存操做访问到硬盘上的文件呢?那就要看内存映射以后的几个相关的过程了。spa
mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样之后,进程无需再调用read或write对文件进行读写,而只须要经过ptr就可以操做文件。可是ptr所指向的是一个逻辑地址,要操做其中的数据,必须经过MMU将逻辑地址转换成物理地址,如图1中过程2所示。这个过程与内存映射无关。设计
前面讲过,创建内存映射并无实际拷贝数据,这时,MMU在地址映射表中是没法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,若是找不到(也就是该文件历来没有被读入内存的状况),则会经过mmap()创建的映射关系,从硬盘上将文件读取到物理内存中,如图1中过程3所示。这个过程与内存映射无关。unix
若是在拷贝数据时,发现物理内存不够用,则会经过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,如图1中过程4所示。这个过程也与内存映射无关。指针
效率
从代码层面上看,从硬盘上将文件读入内存,都要通过文件系统进行数据拷贝,而且数据拷贝操做是由文件系统和硬件驱动实现的,理论上来讲,拷贝数据的效率是同样的。可是经过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为何呢?缘由是read()是系统调用,其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,如图2中过程1,而后再将这些数据拷贝到用户空间,如图2中过程2,在这个过程当中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,因为mmap()将文件直接映射到用户空间,因此中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。所以,内存映射的效率要比read/write效率高。进程
图2.read系统调用原理内存
下面这个程序,经过read和mmap两种方法分别对硬盘上一个名为“mmap_test”的文件进行操做,文件中存有10000个整数,程序两次使用不一样的方法将它们读出,加1,再写回硬盘。经过对比能够看出,read消耗的时间将近是mmap的两到三倍。get
1 #include
2
3 #include
4
5 #include
6
7 #include
8
9 #include
10
11 #include
12
13 #include
14
15 #include
16
17 #include
18
19
20
21 #define MAX 10000
22
23
24
25 intmain()26
27 {28
29 int i=0;30
31 int count=0, fd=0;32
33 structtimeval tv1, tv2;34
35 int *array = (int *)malloc( sizeof(int)*MAX );36
37
38
39 /*read*/
40
41
42
43 gettimeofday( &tv1, NULL );44
45 fd = open( "mmap_test", O_RDWR );46
47 if( sizeof(int)*MAX != read( fd, (void *)array, sizeof(int)*MAX ) )48
49 {50
51 printf( "Reading data failed.../n");52
53 return -1;54
55 }56
57 for( i=0; i
59
60
61 ++array[ i ];62
63 if( sizeof(int)*MAX != write( fd, (void *)array, sizeof(int)*MAX ) )64
65 {66
67 printf( "Writing data failed.../n");68
69 return -1;70
71 }72
73 free( array );74
75 close( fd );76
77 gettimeofday( &tv2, NULL );78
79 printf( "Time of read/write: %dms/n", tv2.tv_usec-tv1.tv_usec );80
81
82
83 /*mmap*/
84
85
86
87 gettimeofday( &tv1, NULL );88
89 fd = open( "mmap_test", O_RDWR );90
91 array = mmap( NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);92
93 for( i=0; i
95
96
97 ++array[ i ];98
99 munmap( array, sizeof(int)*MAX );100
101 msync( array, sizeof(int)*MAX, MS_SYNC );102
103 free( array );104
105 close( fd );106
107 gettimeofday( &tv2, NULL );108
109 printf( "Time of mmap: %dms/n", tv2.tv_usec-tv1.tv_usec );110
111
112
113 return 0;114
115 }
输出结果:
Time of read/write: 154ms
Time of mmap: 68ms