Android 内存映射mmap浅谈

发布于:2021-12-02 01:58:45

Android通信系列目录


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




内存映射mmap
前言Linux 内存层构用户空间与内核空间mmap内存映射原理mmap的使用映射内存的分配总结


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



前言

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


首先对Linux内核内存做一些知识科普


Linux 内存层构

linux中的内存我们也常称之为 内核空间 。以一次磁盘中读请求为例,展示其经历的内存层次。

层次大约分为7层
虚拟文件系统层
作用是屏蔽下层具体文件系统操作的差异,为上层的操作提供一个统一的接口。
**文件系统层 **
具体的文件系统层,一个文件系统一般使用块设备上一个独立的逻辑分区。
Page Cache (层页高速缓存层)
引入 Cache 层的目的是为了提高 Linux 操作系统对磁盘访问的性能。Cache层会缓存内存中的部分数据,当请求到达时,如果Cache中有数据就会直接将数据返回,米面对底层磁盘的操作,提高性能。
通用块层
作用是接收上层发出的磁盘请求,并最终发出 I/O 请求。
I/O 调度层
作用是管理块设备的请求队列。
块设备驱动层
利用驱动程序,驱动具体的物理块设备。
物理块设备层
具体的物理磁盘块。


Cache内存


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


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


Cache中的数据是可以被回收的,当内存空间不够时,就会根据一定规则将部分数进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
进程间,内核空间的数据可共享,所以内核空间 = 可共享空间据写入到磁盘中,所以Linu系统的内存通常是MemFree+Cache



用户空间与内核空间

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


这里有两个隔离,一个进程间是相互隔离的,二是进程内有用户和内核的隔离。进程间的交互就叫进程间通信(IPC,或称跨进程通信),而进程内的用户和内核的交互就是系统调用


进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间进程间,内核空间的数据可共享,所以内核空间 = 可共享空间所有进程共用1个内核空间

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


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


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


mmap内存映射原理

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


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


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


1. 传统Linux文件读写


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


特点:


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


2. mmap文件读写流程


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

    1. 在当前进程的虚拟地址空间中,寻找一段满足要求的虚拟地址
    2. 为此虚拟地址分配一个虚拟内存区域,vm_area_struct结构
    3. 初始化该虚拟内存区域
    4. 插入该虚拟内存区域到进程的虚拟地址区域链表中

    内核同样收到请求后会调用内核的mmap函数,实现地址映射关系配对,即进程虚拟地址空间<< >>文件磁盘地址 关系映射,该映射与内核内存没有任何关联

    1. 进程调用mmap函数,内核同样会得到消息,最终内核调用自身的系统调用函数mmap。(两mmap函数不一样)
    2. 内核mmap函数通过`虚拟文件系统`定位到文件磁盘物理地址。
    3. 通过remap_pfn_range()建立页表,实现了文件地址和虚拟地址区域的映射关系。

    进程的读/写操作访问虚拟地址空间这一段地址,如果读写操作该改变了虚拟地址空间内容,则一段时间后系统会自动回写脏页面到对应的磁盘地址中,即完成了写入文件的操作。

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



特点:


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

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



mmap的使用

mmap的函数位于内核的 头文件中,与其相关的几个函数也列出如下:


// 用户进程调用, 函数用于将文件映射到内存
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的使用场景


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

    内存分配
    我们使用c库的malloc申请内存,malloc的分配内存有两个系统调用,一个brk,另一个就是mmap。

    mmap不仅可以映射文件,也可以映射内存,当mmap使用的flag是MAP_ANONYMOUS,称为建立匿名映射,此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。匿名映射存储的数据就是在物理内存上,不属于任何文件。malloc分配内存底层就是用mmap的匿名映射来操作的。

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


映射内存的分配

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


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


总结

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



相关链接


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

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

相关推荐

最新更新

猜你喜欢