mmap技术研究及应用

一、简介:

mmap是一种内存映射文件的方法。即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享

二、BIO(常规文件操作或传统IO)和mmap区别:

总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。

image.png

三、优缺点

优点:(高性能,操作文件就像操作内存一下,适合对较大文件的读写)
①对文件的读写操作跨国也页缓存,减少数据的拷贝次数,用内存读写取代IO流读写,提高了文件读写效率(Andorid加载.dex文件也通过使用此技术);
②实现用户空间和内核空间的高效交互方式;
③提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。
④实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助磁盘空间协助操作,补充内存的不足。但是进一步会照成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好解决,需要用磁盘空间替代内存的时候,mmap都可以发挥其功效

缺点:①文件如果很小,比如小于4K的,比如60bytes,由于在内存当中的组织都是按页组织的,将文件调入到内存当中是一个页4K,相当于4096-60=4036bytes的内存空间浪费掉了;②文件无法完成拓展,因为mmap到内存的时候,你所能操作的范围就已经确定了,无法增加文件长度。
使用场景:
①对同一块区域频繁读写操作;
②用户日志、数据上报等,微信开源mars框架中的xlog模块就是基于mmap特性实现;
③跨进程同步的时候,mmap是个不错的选择,Android跨进程通信有自己独有的Binder机制,内部使用mmap实现;
Java层面使用:MappedByteBuffer已经封装好
C++代码实现:mmap

四、内存映射原理

  • 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
  • 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
  • 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

五、mmap相关函数

①建立映射关系函数:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
成功执行时,mmap()返回被映射区的指针地址。失败时,mmap()返回MAP_FAILED[其值为(void *)-1]
②解除映射关系函数:
int munmap( void * addr, size_t len )
成功执行时,munmap()返回0。失败时,munmap返回-1,error返回标志和mmap一致;
addr是调用mmap()时返回的地址,len是映射区的大小;
③实时同步写入:
int msync( void *addr, size_t len, int flags )
一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。
可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

六、mmap使用细节

mmap映射区域大小必须是物理页大小(page_size)的倍数(32位系统中通常是4k字节),原因是内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位;
②内核可以跟踪被内存映射的底层对象(文件)的大小,进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节
③映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。

七、使用:

①C++使用mmap读写文件:

//-------------------------------------------读取文件----------------------------------
// 打开文件
int fd = open("input.txt", O_RDONLY);  
// 读取文件长度
int len = lseek(fd,0,SEEK_END);  
// 建立内存映射
char *addr = (char *) mmap(NULL, len, PROT_READ, MAP_PRIVATE,fd, 0);      
close(fd);
// data用于保存读取的数据
char* data; 
// 复制过来
memcpy(data, addr, len);
// 解除映射
munmap(addr, len)

//-------------------------------------------写入文件----------------------------------
//假设写入的数据放在char* data中
int len = data.length();
// 打开文件
int fd=open("output.txt", O_RDWR|O_CREAT, 00777);
// lseek将文件指针往后移动file_size-1位
lseek(fd,len-1,SEEK_END);  
// 从指针处写入一个空字符;mmap不能扩展文件长度,这里相当于预先给文件长度,准备一个空架子
write(fd, "", 1);
// 使用mmap函数建立内存映射
char* addr = (char*)mmap(NULL, len, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
// 内存映射建立好了,此时可以关闭文件了
close(fd);
// 把data复制到addr里
memcpy(addr, data, len);
// 解除映射
munmap(addr, len)

①Java使用NIO库中的MappedByteBuffer实现mmap读写文件:

try {
           byte[] originalByte = "你好奥,我是好人".getBytes();
           RandomAccessFile raf = new RandomAccessFile(file, "rw");
           ////position映射文件的起始位置,size映射文件的大小
           MappedByteBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024);
           //写入数据
           map.put(originalByte);

           byte[] newData= new byte[originalByte.length];
           //获取数据
           map.get(newData);
           Log.d(TAG, "data:" + String.valueOf(newData));
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • Linux进程通信实现机制有很多,也有各自优缺点和适用场景,关于她们之间的对比,等各种通信机制一一介绍后,再来一个...
    batbattle阅读 4,063评论 3 13
  • ​通信是Android开发必不可少的一部分,不管是我们做应用App开发,还是Android系统,都使用了大量的通信...
    子者不语阅读 395评论 0 1
  • 1.目前很多博客说的五种模型都是从read角度来描述的。 2.我们也常会说Direct IO,或者其他文件IO。他...
    简书徐小耳阅读 1,618评论 0 5
  • C++虚函数: 多态: 静态多态(重载)、动态多态(虚函数) 虚函数 虚函数表:编译器为每个类创建了一个虚函数表...
    Supreme_DJK阅读 886评论 0 0
  • 前言 看这篇文章之前需要知道一个概念 虚拟内存系统通过将虚拟内存分割为称作虚拟页(Virtual Page,VP)...
    叫我不矜持阅读 63,708评论 7 29