android mmap

android mmap的使用
Android-内存映射mmap

mmap内存映射原理
mmap是一种内存映射文件的方法,它将一个文件映射到进程的地址空间中,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。如下图所示:
————————————————版权声明:本文为CSDN博主「mcryeasy」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/mcryeasy/article/details/86741781

mmap内存映射具体流程如下:

1、用户进程调用内存映射函数库mmap,当前进程在虚拟地址空间中,寻找一段空闲的满足要求的虚拟地址。

2、此时内核收到相关请求后会调用内核的mmap函数,注意,不同于用户空间库函数。内核mmap函数通过虚拟文件系统定位到文件磁盘物理地址,既实现了文件地址和虚拟地址区域的映射关系。 此时,这片虚拟地址并没有任何数据关联到主存中。

注意,前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

3、进程的读或写操作访问虚拟地址空间这一段映射地址,现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页中断。

4、由于引发了缺页中断,内核则调用nopage函数把所缺的页从磁盘装入到主存中。

5、之后用户进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

注意:这里拷贝磁盘内容到主存,这里的主存是指处于内核空间的Page Cache,而不是用户空间的内存。用户地址要访问内核空间中的数据,需使用MMU把虚拟地址映射到内核的内存地址中,即可对数据进行操作。整个mmap工作流程大体如下:
————————————————版权声明:本文为CSDN博主「mcryeasy」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/mcryeasy/article/details/86741781

这里我们可以看出mmap系统调用与read/write调用的区别在于:

mmap只需要一次系统调用(一次拷贝),后续操作不需要系统调用。
访问的数据不需要在page cache和用户缓冲区之间拷贝。 访问的数据不需要在page cache和用户缓冲区之间拷贝。
从上所述,当频繁对一个文件进行读取操作时,mmap会比read/write更高效。

mmap的应用场景
mmap在Linux、Android系统上有非常多的应用场景。

1、Linux进程的创建
Linux执行一个程序,这个程序在磁盘上,为了执行这个程序,需要把程序加载到内存中,这时也是采用的是mmap。你可以从/proc/pid/maps看到每个进程的mmap状态。

2、内存分配
我们使用c库的malloc申请内存,malloc的分配内存有两个系统调用,一个brk,另一个就是mmap。其实mmap不仅可以映射文件,也可以映射内存,当mmap使用的flag是MAP_ANONYMOUS,称为建立匿名映射,此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。匿名映射存储的数据就是在物理内存上,不属于任何文件。malloc分配内存底层就是用mmap的匿名映射来操作的。

3、Binder进程间通信
了解进程间通信的人都知道Android使用的是Binder进行进程间通信,它的效率高于Linux其他传统的进程间通信,因为它只要一次拷贝,而之所以只需要进行一次拷贝的原因就在于使用了mmap!

一次完整的 Binder IPC 通信过程通常是这样:

Server端在启动之后,调用对/dev/binder设备调用mmap。
内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后在Server端的用户空间和内核空间同时进行映射。内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后在Server端的用户空间和内核空间同时进行映射
Client发送请求,这个请求将先到驱动中,同时需要将数据从Client进程的用户空间拷贝(Client发送请求,这个请求将先到驱动中,同时需要将数据从Client进程的用户空间拷贝(copy_from_user)到内核空间。
驱动通过请求通知Server端有人发出请求,Server进行处理。由于内核空间和Server端进程的用户空间存在内存映射,因此Server进程的代码可以直接访问。这样便完成了一次进程间的通信。
在这里插入图片描述

4、IO读写效率
mmap最主要的功能就是提高了IO读写的效率,微信的MMKV key-value组件、美团的 Logan的日志组件 都是基于mmap来实现的。在微信的 MMKV/Android/MMKV/mmkv/src/main/cpp/MMKV.cpp 和美团的 Meituan-Dianping/Logan/blob/master/Logan/Clogan/mmap_util.c 的这两个文件中你都可以看到对mmap函数的使用,有兴趣的小伙伴可以自行查阅。

发布了18 篇原创文章 · 获赞 2 · 访问量 3140

猜你喜欢

转载自blog.csdn.net/shiningdreamercaihua/article/details/104828344
今日推荐