Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(1)

简介:
    在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享数据的机制:匿名共享内存,它能够辅助内存管理系统来有效地管理内存,它的实现原理我们在前面已经分析过了。为了方便使用匿名共享内存机制,系统还提供了Java调用接口(MemoryFile)和C++调用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已经分析过了,本文中将继续分析它的C++接口。
        在前面一篇文章 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析 中,我们分析了匿名共享内存驱动程序Ashmem的实现,重点介绍了它是如何辅助内存管理系统来有效地管理内存的,简单来说,它就是给使用者提供锁机制来辅助管理内存,当我们申请了一大块匿名共享内存时,中间过程有一部分不需要使用时,我们就可以将这一部分内存块解锁,这样内存管理系统就可以把它回收回去了。接着又在前面一篇文章 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析 中,我们分析了匿名共享内存是如何通过Binder进程间通信机制来实现在进程间共享的,简单来说,就是每一个匿名共享内存块都是一个文件,当我们需要在进程间共享时,就把这个文件的打开描述符通过Binder进程间通信机制传递给另一外进程,在传递的过程中,Binder驱动程序就通过这个复制这个打开文件描述符到目标进程中去,从而实现数据共享。在文章 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划 中,我们介绍了如何在Android应用程序中使用匿名共享内存,主要是通过应用程序框架层提供的MemoryFile接口来使用的,而MemoryFile接口是通过JNI方法调用到系统运行时库层中的匿名共享内存C接口,最终通过这些C接口来使用内核空间中的匿名共享内存驱动模块。为了方便开发者灵活地使用匿名共享内存,Android系统在应用程序框架层中还提供了使用匿名共享内存的C++接口,例如,Android应用程序四大组件之一Content Provider,它在应用程序间共享数据时,就是通过匿名共享内存机制来实现,但是它并不是通过MemoryFile接口来使用,而是通过调用C++接口中的MemoryBase类和MemoryHeapBase类来使用。在接下来的内容中,我们就详细分析MemoryHeapBase类和MemoryBase类的实现,以及它们是如何实现在进程间共享数据的。
        如果我们想在进程间共享一个完整的匿名共享内存块,可以通过使用MemoryHeapBase接口来实现,如果我们只想在进程间共享一个匿名共享内存块中的其中一部分时,就可以通过MemoryBase接口来实现。MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,它们都可以作为一个Binder对象来在进程间传输,因此,希望读者在继续阅读本文之前,对Android系统的Binder进程间通信机制有一定的了解,具体可以参考前面一篇文章 Android进程间通信(IPC)机制Binder简要介绍和学习计划 。下面我们就首先分析MemoryHeapBase接口的实现,然后再分析MemoryBase接口的实现,最后,通过一个实例来说明它们是如何使用的。
        1. MemoryHeapBase
         前面说到,MemoryHeapBase类的对象可以作为Binder对象在进程间传输,作为一个Binder对象,就有Server端对象和Client端引用的概念,其中,Server端对象必须要实现一个BnInterface接口,而Client端引用必须要实现一个BpInterface接口。下面我们就先看一下MemoryHeapBase在Server端实现的类图:

         这个类图中的类可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括MemoryHeapBase、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BnInterface、BnMemoryHeap、IBinder、BBinder、ProcessState和IPCThreadState七个类。
        我们先来看跟匿名共享内存业务相关的这部分类的逻辑关系。IMemoryBase定义了匿名共享内操作的接口,而MemoryHeapBase是作为Binder机制中的Server角色的,因此,它需要实现IMemoryBase接口,此外,MemoryHeapBase还继承了RefBase类。从前面一篇文章 Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析 中,我们知道,继承了RefBase类的子类,它们的对象都可以结合Android系统的智能指针来使用,因此,我们在实例化MemoryHeapBase类时,可以通过智能指针来管理它们的生命周期。
        再来看和Binder机制相关的这部分类的逻辑关系。从 Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析 这篇文章中,我们知道,所有的Binder对象都必须实现IInterface接口,无论是Server端实体对象,还是Client端引用对象,通过这个接口的asBinder成员函数我们可以获得Binder对象的IBinder接口,然后通过Binder驱动程序把它传输给另外一个进程。当一个类的对象作为Server端的实体对象时,它还必须实现一个模板类BnInterface,这里负责实例化模板类BnInterface的类便是BnMemoryHeap类了,它里面有一个重要的成员函数onTransact,当Client端引用请求Server端对象执行命令时,Binder系统就会调用BnMemoryHeap类的onTransact成员函数来执行具体的命令。当一个类的对象作为Server端的实体对象时,它还要继承于BBinder类,这是一个实现了IBinder接口的类,它里面有一个重要的成员函数transact,当我们从Server端线程中接收到Client端的请求时,就会调用注册在这个线程中的BBinder对象的transact函数来处理这个请求,而这个transact函数会将这些Client端请求转发给BnMemoryHeap类的onTransact成员函数来处理。最后,ProcessState和IPCThreadState两个类是负责和Binder驱动程序打交道的,其中,ProcessState负责打开Binder设备文件/dev/binder,打开了这个Binder设备文件后,就会得到一个打开设备文件描述符,而IPCThreadState就是通过这个设备文件描述符来和Binder驱动程序进行交互的,例如它通过一个for循环来不断地等待Binder驱动程序通知它有新的Client端请求到来了,一旦有新的Client端请求到来,它就会调用相应的BBinder对象的transact函数来处理。
        本文我们主要是要关注和匿名共享内存业务相关的这部分类,即IMemoryBase和MemoryHeapBase类的实现,和Binder机制相关的这部分类的实现,可以参考 Android进程间通信(IPC)机制Binder简要介绍和学习计划 一文。
        IMemoryBase类主要定义了几个重要的操作匿名共享内存的方法,它定义在frameworks/base/include/binder/IMemory.h文件中:

  
  
  1. class IMemoryHeap : public IInterface   
  2. {   
  3. public:   
  4.     ......   
  5.    
  6.     virtual int         getHeapID() const = 0;   
  7.     virtual void*       getBase() const = 0;   
  8.     virtual size_t      getSize() const = 0;   
  9.    
  10.     ......   
  11. };   

       成员函数getHeapID是用来获得匿名共享内存块的打开文件描述符的;成员函数getBase是用来获得匿名共享内存块的基地址的,有了这个地址之后,我们就可以在程序里面直接访问这块共享内存了;成员函数getSize是用来获得匿名共享内存块的大小的。
        MemoryHeapBase类主要用来实现上面IMemoryBase类中列出来的几个成员函数的,这个类声明在frameworks/base/include/binder/MemoryHeapBase.h文件中:

  
  
  1. class MemoryHeapBase : public virtual BnMemoryHeap   
  2. {   
  3. public:   
  4.     ......   
  5.    
  6.     /*  
  7.     * maps memory from ashmem, with the given name for debugging  
  8.     */   
  9.     MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);   
  10.    
  11.     ......   
  12.    
  13.     /* implement IMemoryHeap interface */   
  14.     virtual int         getHeapID() const;   
  15.     virtual void*       getBase() const;   
  16.     virtual size_t      getSize() const;   
  17.    
  18.     ......   
  19. private:   
  20.     int         mFD;   
  21.     size_t      mSize;   
  22.     void*       mBase;   
  23.    
  24.     ......   
  25. }   
        MemoryHeapBase类的实现定义在frameworks/base/libs/binder/MemoryHeapBase.cpp文件中,我们先来看一下它的构造函数的实现:

  
  
  1. MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)   
  2. : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),   
  3. mDevice(0), mNeedUnmap(false)   
  4. {   
  5.     const size_t pagesize = getpagesize();   
  6.     size = ((size + pagesize-1) & ~(pagesize-1));   
  7.     int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : namesize);   
  8.     LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));   
  9.     if (fd >= 0) {   
  10.         if (mapfd(fd, size) == NO_ERROR) {   
  11.             if (flags & READ_ONLY) {   
  12.                 ashmem_set_prot_region(fd, PROT_READ);   
  13.             }   
  14.         }   
  15.     }   
  16. }   
      这个构造函数有三个参数,其中size表示要创建的匿名共享内存的大小,flags是用来设置这块匿名共享内存的属性的,例如是可读写的还是只读的,name是用来标识这个匿名共享内存的名字的,可以传空值进来,这个参数只是作为调试信息使用的。

 
        MemoryHeapBase类创建的匿名共享内存是以页为单位的,页的大小一般为4K,但是是可以设置的,这个函数首先通过getpagesize函数获得系统中一页内存的大小值,然后把size参数对齐到页大小去,即如果size不是页大小的整数倍时,就增加它的大小,使得它的值为页大小的整数倍:

  
  
  1. const size_t pagesize = getpagesize();   
  2. size = ((size + pagesize-1) & ~(pagesize-1));   

调整好size的大小后,就调用系统运行时库层的C接口ashmem_create_region来创建一块共享内存了:

  
  
  1. int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : namesize);   
       这个函数我们在前面一篇文章 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析 中可以介绍过了,这里不再详细,它只要就是通过Ashmem驱动程序来创建一个匿名共享内存文件,因此,它的返回值是一个文件描述符。

 
        得到了这个匿名共享内存的文件描述符后,还需要调用mapfd成函数把它映射到进程地址空间去:

  
  
  1. status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)   
  2. {   
  3.     ......   
  4.    
  5.     if ((mFlags & DONT_MAP_LOCALLY) == 0) {   
  6.         void* base = (uint8_t*)mmap(0, size,   
  7.             PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);   
  8.         ......   
  9.         mBase = base;   
  10.         ......   
  11.     } else  {   
  12.         ......   
  13.     }   
  14.    
  15.     mFD = fd;   
  16.     mSize = size;   
  17.     return NO_ERROR;   
  18. }   

     一般我们创建MemoryHeapBase类的实例时,都是需要把匿名共享内存映射到本进程的地址空间去的,因此,这里的条件(mFlags & DONT_MAP_LOCALLY == 0)为true,于是执行系统调用mmap来执行内存映射的操作。

  
  
  1. void* base = (uint8_t*)mmap(0, size,   
  2.     PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);   
        传进去的第一个参数0表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置,第二个参数size表示要映射的匿名共享内文件的大小,第三个参数PROT_READ|PROT_WRITE表示这个匿名共享内存是可读写的,第四个参数fd指定要映射的匿名共享内存的文件描述符,第五个参数offset表示要从这个文件的哪个偏移位置开始映射。调用了这个函数之后,最后会进入到内核空间的ashmem驱动程序模块中去执行ashmem_map函数,这个函数的实现具体可以参考 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析 一文,这里就不同详细描述了。调用mmap函数返回之后,就得这块匿名共享内存在本进程地址空间中的起始访问地址了,将这个地址保存在成员变量mBase中,最后,还将这个匿名共享内存的文件描述符和以及大小分别保存在成员变量mFD和mSize中。




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966895,如需转载请自行联系原作者
目录
相关文章
|
3月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
128 26
|
8月前
|
存储 程序员 编译器
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
4月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
61 1
|
5月前
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
181 1
|
10月前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
407 68
|
7月前
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
8月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
346 0
|
9月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
11月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
384 16
|
10月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。

热门文章

最新文章