mmap映射空间与内存和磁盘的IO性能对比

mmap

功能描述:mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。 基于文件的映射,在mmapmunmap执行过程的任何时刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情况下没有得到更新,首次对映射区的第一个页索引时会更新该字段的值。用PROT_WRITE MAP_SHARED标志建立起来的文件映射,其st_ctime st_mtime

在对映射区写入之后,但在msync()通过MS_SYNC MS_ASYNC两个标志调用之前会被更新。

用法:

#include

void *mmap(void *start, size_t length, int prot, int flags,

int fd, off_t offset);

int munmap(void *start, size_t length);

mmap能够把磁盘文件映射进内存,这样对文件的操作就可以直接像操作内存那样,而不用read()/write()函数了,那么通过mmap映射的空间和内存、磁盘的IO性能上有何差异呢?为此,写了如下程序测试mmap映射的空间、内存已经磁盘的读写速度。

程序代码如下(mmap_test.c):

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define SIZE 50*1024*1024

int main(int argc, char** argv)

{

         FILE *fp;

         char c = 'a';

         int i;

         char *pmap;

         int fd;

         char *mem;

         struct timeval t1, t2;

         long spend_us;

         printf("write size: %dMB/n", SIZE/1024/1024);

         if((fp = fopen("disk_file", "w")) == NULL)

         {

                   printf("fopen error!/n");

                   return -1;

         }

         gettimeofday(&t1, NULL);

         for(i = 0; i < SIZE; i++)

         {

                   if(fwrite(&c, sizeof(char), 1, fp) != 1)

                            printf("file write error!/n");

         }

         gettimeofday(&t2, NULL);

         spend_us = 1000000 * (t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec);

         printf("write disk file,spend time:%ld us/n", spend_us);

         fclose(fp);

 

         fd = open("mmap_file", O_RDWR);

         if(fd < 0){

                   perror("open mmap_file");

                   return -1;

         }

         pmap = (char *)mmap(NULL, sizeof(char) * SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

         if(pmap == MAP_FAILED){

                   perror("mmap");

                   return -1;

         }

         gettimeofday(&t1, NULL);

         for(i = 0; i < SIZE; i++)

         {

                   memcpy(pmap + i, &c, 1);

         }

         gettimeofday(&t2, NULL);

         spend_us = 1000000 * (t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec);

         printf("write mmap file,spend time:%ld us/n", spend_us);

         close(fd);

         munmap(pmap, sizeof(char) * SIZE);

         mem = (char *)malloc(sizeof(char) * SIZE);

         if(!mem)

         {

                   printf("mem malloc error!/n");

                   return -1;

         }

         gettimeofday(&t1, NULL);

         for(i = 0; i < SIZE; i++)

         {

                   memcpy(mem + i, &c, 1);

         }

         gettimeofday(&t2, NULL);

         spend_us = 1000000 * (t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec);

         printf("write mem,spend time:%ld us/n", spend_us);

         free(mem);

         return 0;

}

编译程序:gcc –o mmap_test mmap_test.c

运行之前在当前目录创建disk_filemmap_file

touch disk_file

dd if=/dev/zero of=mmap_file bs=1M count=50

运行程序:./mmap_test

运行结果:

write size: 50MB

write disk file,spend time:3020481 us

write mmap file,spend time:793141 us

write mem,spend time:399923 us

计算写的速度:

写磁盘速度:16.55M/s

mmap映射的空间速度:63.04M/s

写内存的速度:125.02M/s

由此可见,由mmap映射的空间的读写速度比直接读写磁盘的速度快。

那么,为什么会快呢?这就需要从mmap实现的原理来分析了。

在此图中,起始地址mmap的返回值。在图中,映射存储区位于堆和栈之间

以下摘自博客

http://hi.baidu.com/%D4%BC%D0%DE%D1%C7ing/blog/item/ee1664d899820ae138012f11.html

内核怎样保证各个进程寻址到同一个共享内存区域的内存页面

1 page cacheswap cache中页面的区分:一个被访问文件的物理页面都驻留在page cacheswap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cacheswap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。

2、文件与 address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。

3、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

4、对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区 (swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。

注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。

5、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。

你可能感兴趣的:(linux,kernel)