Android通信系列目录

  1. Android Binder通信原理详解
  2. Android 内存映射mmap浅谈

内存映射mmap

  • 前言
  • Linux 内存层构
  • 用户空间与内核空间
  • 内存映射原理
  • mmap的使用
  • mmap优缺点
  • 总结

博客创建时间:2020.11.01
博客更新时间:2022.10.06

以Android studio build=7.0.0,SDKVersion 31来分析讲解。如图文和网上其他资料不一致,可能是别的资料版本较低而已。


前言

最能想到mmap函数的是跨进程通信Binder机制中使用到了,它的强大之处在于通过内存映射直接对文件进行读写,减少了对数据的拷贝次数和提高了IO读写的效率。

Linux 内存层构

首先对Linux内核内存做一些知识科普。linux中的内存我们也常称之为 内核空间 。以一次磁盘中读请求为例,展示其经历的内存层次。

七层结构

虚拟文件系统层
作用是屏蔽下层具体文件系统操作的差异,为上层的操作提供一个统一的接口。

文件系统层
具体的文件系统层,一个文件系统一般使用块设备上一个独立的逻辑分区。

Page Cache (层页高速缓存层)
引入 Cache 层的目的是为了提高 Linux 操作系统对磁盘访问的性能。Cache层会缓存内存中的部分数据,当请求到达时,如果Cache中有数据就会直接将数据返回,米面对底层磁盘的操作,提高性能。

通用块层
作用是接收上层发出的磁盘请求,并最终发出 I/O 请求。

I/O 调度层
作用是管理块设备的请求队列。

块设备驱动层
利用驱动程序,驱动具体的物理块设备。

物理块设备层
具体的物理磁盘块。


Cache内存
Page Cache层,也称页缓存、内核空间。实际上就是内核中的物理内存,在磁盘和用户空间之间多了一层缓存层,由内核负责管理控制。由于物理内存的速度远远快于磁盘的速度,数据放入Page Cache中可以更快的进行访问。

数据被访问后,短时间内有极大会再一次被访问,在短时间内集中访问同一数据的原理就叫做局部性原理

Cache中的数据是可以被回收的,当内存空间不够时,就会根据一定规则将部分数据回收。


用户空间与内核空间

Linux的进程是相互独立的,一个进程是不能直接操作或者访问别一个进程空间的。每个进程空间还分为用户空间和内核(Kernel)空间,相当于把Kernel和上层的应用程序抽像的隔离开。

用户空间内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

这里有两个隔离,一个进程间是相互隔离的,二是进程内有用户空间和内核空间的隔离。

  • 进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
  • 进程间,内核空间的数据可共享,所以内核空间 = 可共享空间,所以Linu系统的内存通常是MemFree+Cache
  • 所有进程共用1个内核空间

系统调用

进程间的交互就叫进程间通信(IPC,或称跨进程通信),而进程内的用户和内核的交互就是系统调用

用户空间访问内核空间的唯一方式就是系统调用;通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。

系统调用示例
进程内用户空间内核空间 进行交互需通过系统调用两个函数。

  • copy_from_user():将用户空间的数据拷贝到内核空间
  • copy_to_user():将内核空间的数据拷贝到用户空间

内存映射原理

mmap是一种内存映射文件的方法,它将一个文件映射到进程的地址空间中,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。

当磁盘地址和进程虚拟地址建立关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写数据到磁盘上,即直接完成了对文件的操作而不必在调用read/write等系统调用函数。同样的如果磁盘中内容有修改,也会直接反映到用户空间其数据改变了。

所以通过mmap映射方式可以使不同进程间共享磁盘文件,其共享对象可为普通文件或匿名文件

映射内存的分配

mmap映射区域大小必须是物理页大小(page_size)的整倍数(在Linux中内存页通常是4k)。因为内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作,mmap从磁盘到虚拟地址空间的映射也必须是页。

例如,有一个文件的大小是5K,mmap函数从文件的起始位置映射5K到虚拟内存中,由于内存物理页是4K,虽然映射的文件只有5K,但是实际上映射到内存区域的内存是8K,以便满足物理页大小的整数倍。映射后对5~8K的内存区域用零填充,对这部分的操作不会报错也不会写入到原文件中。

传统I/O读写流程

  1. 用户进程发起文件数据的读请求
  2. 内核通过查找进程文件符表,定位内核已打开文件集上的文件信息,从而找到文件inode
  3. inode在address_space上查找要请求的文件页是否已缓存在页缓存中
  4. 如已在缓存页中,则直接返回这片文件页上的内容
  5. 如不在缓存页上,就会引发缺页中断。 当发生缺页中断时,内核则调用nopage函数把所缺的页从磁盘装入到内存内核中及Page Cache中。接着再发起读页面过程,从而将数据从页缓存中拷贝到用户空间中


特点:

  1. 常规文件操作为了读写效率和保护磁盘,使用了页缓存机制
  2. 页缓存处在内核空间中,不能直接被用户进程直接寻址,需要将数据从页缓存中拷贝到主内存

mmap读写流程

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

     1. 在当前进程的虚拟地址空间中,寻找一段满足要求的虚拟地址2. 为此虚拟地址分配一个虚拟内存区域,vm_area_struct结构3. 初始化该虚拟内存区域4. 插入该虚拟内存区域到进程的虚拟地址区域链表中
    
  2. 内核同样收到请求后会调用内核的mmap函数,实现地址映射关系配对,即进程虚拟地址空间<< >>文件磁盘地址 关系映射,该映射与内核内存没有任何关联

     1. 进程调用mmap函数,内核同样会得到消息,最终内核调用自身的系统调用函数mmap。(两mmap函数不一样)2.  内核mmap函数通过`虚拟文件系统`定位到文件磁盘物理地址。3. 通过remap_pfn_range()建立页表,实现了文件地址和虚拟地址区域的映射关系。
    
  3. 进程的读/写操作访问虚拟地址空间这一段地址,如果读写操作该改变了虚拟地址空间内容,则一段时间后系统会自动回写脏页面到对应的磁盘地址中,即完成了写入文件的操作。

    修改的脏页面不会立即更新,而是有延时,可以通过msync()来强制同步。通过此法能将所写的内容立即保存到磁盘中
    

特点:

  1. 用户空间内核空间磁盘块通过映射直接交互,不在间接通过页缓存
  2. 文件读写操作跨过了页缓存,数据拷贝次数减少为只需一次
  3. 借助硬盘的大空间,对于大规模数据的读写避免对页内存空间大小的依赖,提高操作效率。

mmap数据读写的性能提升就在于对数据的读写拷贝次数,mmap只需要一次系统调用(一次拷贝),后续操作不需要系统调用。并且访问的数据不需要在page cache和用户缓冲区之间拷贝。


mmap读写优势

  1. 对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。

  2. 实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉。

  3. 提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。

     如果进程A和进程B都映射了区域C,当A第一次读取C时通过缺页从磁盘复制文件页到内存中;但当B再读C的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。
    
  4. 可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。


mmap的使用

mmap的函数位于内核的<sys/mman.h> 头文件中,与其相关的几个函数也列出如下:

// 用户进程调用,    函数用于将文件映射到内存
void* mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset);// 函数用于取消映射,进程在映射空间对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap() 后才执行该操作。
int munmap(void *addr, size_t length);
// 函数用于实现磁盘文件内容与共享内存区中的内容一致,即同步操作。
// 除了调用munmap取消映射,我们也可以调用msync()实现磁盘上文件内容与内核内存的内容一致
int msync(void * addr, size_t len, int flags);

mmap的使用场景

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。


mmap优缺点

Linux将虚拟内存与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程就是内存映射(memory mapping)

对文件进行mmap后,会在进程的虚拟内存分配地址空间,创建映射关系。这样就可以采用指针的方式来读写操作这一段内存,而系统会自动回写到对应的文件磁盘上。

I/O在系统的操作流程
虚拟内存被操作系统划分为两块:用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全期间两者是隔离得,即时用户的程序崩溃了,内核也不受影响。

文件读写流程:

  1. 调用write,告诉内核需要写入数据的开始地址和长度
  2. 内核将数据拷贝到内核缓存
  3. 有操作系统调用,将数据拷贝到磁盘,完成写入

优点

  1. mmap 防止数据丢失,提高读写效率
  2. 精简数据,以最少量的数据局量表示最多的信息,减少数据大小
  3. 增量新增,避免每次数据新增时的全量写入
  4. mmap对文件的读写操作只需要对磁盘到用户主存的一次数据拷贝过程,减少了数据的拷贝次数,提高文件读写效率。
  5. mmap使用逻辑内存对磁盘文件进行映射,操作内存就相当于操作文件,不需要开启线程,操作mmap的速度和操作内存的速度一样快。
  6. mmap提供一块随时写入的内存,app只管往里写入数据,由操作系统如内存不足。进程退出时负责将内存写回到文件。不必担心crash导致数据丢失。
  7. mmap的适用场景是大文件的频繁读写,这样就可以节省很多IO的耗时。
  8. 即使进程意外死亡, 也能够通过 Linux 内核的保护机制, 将进行了文件映射的内存数据刷入到文件中, 提升了数据写入的可靠性

缺点:

  1. 因为mmap是按照页存储方式进行存储,每页4096字节,如果数据只有100字节,则正页将有大大的浪费。
  2. 写回文件的工作由系统负责,但是并不是实时的,是定期写回到磁盘的,中间如果发生内核崩溃、断电等,还是会丢失数据,不过可以通过msync将数据同步回磁盘。

总结

本篇博文主要讲解的是mmap内存映射相关知识,主要分析了mmap的原理和使用案例流程分析,在Android中Binder机制中其核心就使用了mmap内存映射。


相关链接

  1. Android Binder通信原理详解
  2. Android 内存映射mmap浅谈

扩展链接:

  1. Android CameraX 使用入门
  2. Android 今日头条屏幕适配详细使用攻略
  3. Android 史上最新最全的ADB及命令百科,没有之一

博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !

Android 内存映射mmap浅谈相关推荐

  1. Android安全开发之浅谈密钥硬编码

    Android安全开发之浅谈密钥硬编码 作者:伊樵.呆狐@阿里聚安全 1 简介 在阿里聚安全的漏洞扫描器中和人工APP安全审计中,经常发现有开发者将密钥硬编码在Java代码.文件中,这样做会引起很大风 ...

  2. linux mmap 内存映射 mmap() vs read()/write()/lseek()

    From: http://www.perfgeeks.com/?p=723 通过strace统计系统调用的时候,经常可以看到mmap()与mmap2().系统调用mmap()可以将某文件映射至内存(进 ...

  3. 进程间通信(IPC)之内存映射mmap和共享内存shm

    一.共享内存shm 1 概念:多个进程的地址空间都映射到同一块物理内存,这样多个进程都能看到这块物理内存,实现进程间通信,而且不需要数据的拷贝,所以速度最快. 二.内存映射mmap 1 前言:先介绍一 ...

  4. android应用前端,Android应用开发之浅谈移动前端适配

    本文将带你了解Android应用开发之浅谈移动前端适配,希望本文对大家学Android有所帮助 1. 什么是前端适配 从UI展现层面上: 我们期望不同尺寸的设备,页面可以自适应的展示或者进行等比缩放, ...

  5. Android安全开发之浅谈加密算法的坑

    Android安全开发之浅谈加密算法的坑 作者:伊樵.舟海@阿里聚安全 Android开发中,难免会遇到需要加解密一些数据内容存到本地文件.或者通过网络传输到其他服务器和设备的问题,但并不是使用了加密 ...

  6. iOS之深入解析文件内存映射MMAP

    一.常规文件操作 常规文件操作(read/write)有以下重要步骤: 进程发起读文件请求: 内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的 inode: inode ...

  7. Binder跨进程通信原理(二):内存映射mmap原理分析

    一直都对内存映射文件这个概念很模糊,不知道它和虚拟内存有什么区别,而且映射这个词也很让人迷茫,今天终于搞清楚了...下面,我先解释一下我对映射这个词的理解,再区分一下几个容易混淆的概念,之后,什么是内 ...

  8. python 共享内存变量_浅谈python多进程共享变量Value的使用tips

    前言: 在使用tornado的多进程时,需要多个进程共享一个状态变量,于是考虑使用multiprocessing.Value(对于该变量的具体细节请查阅相关资料).在根据网上资料使用Value时,由于 ...

  9. 内存映射MMAP和DMA【转】

    转自:http://blog.csdn.net/zhoudengqing/article/details/41654293 版权声明:本文为博主原创文章,未经博主允许不得转载. 这一章介绍Linux内 ...

  10. mmap映射大于4g的文件_iOS文件内存映射——MMAP

    前言 最近一段项目上总是出现一些因为文件没有及时保存而产生的问题,因此小编就在网上寻找到了这个文件存储方法mmap,这里为大家进行下简单的介绍. 简介 首先我们需要对iOS中各App的运行环境进行了解 ...

最新文章

  1. WPF 与Surface 2.0 SDK 亲密接触–LibraryContainer 篇
  2. 第一家公立大学支持比特币支付学费
  3. 【Vista中系统准备工具存在隐患,自动清除用户资料】
  4. 直播报名 | CUDA优化:高性能库cuBLAS使用指南
  5. 详解PostgreSQL数据库中的两阶段锁
  6. python的认识从唯物主义_你对唯物主义的看法是怎么样的?
  7. (转)江南愤青丨丨监管办法之后,网贷一地鸡毛(2016年)
  8. ArcGIS空间统计-局部空间自相关
  9. 2022-2027年中国石油装备制造市场竞争态势及行业投资前景预测报告
  10. 分享到QQ好友 申请腾讯开放平台审核与认证
  11. jpg转bmpbmp转jpg
  12. Unity Prefabs 预设体
  13. 苹果手机浏览器safari不显示手机号问题
  14. 40vf什么意思_LED的基本术语VF
  15. 解决本地新建项目推送到码云失败的问题
  16. 转载与积累系列 - Docker 核心技术与实现原理
  17. EXCEL如何批量更改MAC地址格式?
  18. 对于小白,创建一个网站你需要做什么?
  19. 刘意JavaSE 学习笔记——总纲
  20. 【Stephen Boyd】【2018.08】应用线性代数导论的Julia语言学习手册

热门文章

  1. 计算机软考你真的了解吗?10G+干货全在这里了
  2. 移动端实现HTML5 mp3录音踩坑指南:系统播放音量变小、一些机型录音断断续续 之 MediaRecorder和AudioWorklet的终极对决
  3. uniapp中使用moment.js日期插件
  4. matlab中序列的dtft,dtft的matlab
  5. C#调用电脑的默认图片浏览器软件打开图片
  6. MarkDown简单使用教程
  7. JavaCV 第一个JavaCV程序
  8. 如何搭建一对一直播PHP直播系统源码的流程
  9. CycleGAN与pix2pix详解
  10. 华为HCIE云计算培训笔记第3天