Android系统匿名共享内存Anonymous Shared MemoryC++调用接口分析.docx
- 文档编号:4312763
- 上传时间:2022-11-29
- 格式:DOCX
- 页数:35
- 大小:499.99KB
Android系统匿名共享内存Anonymous Shared MemoryC++调用接口分析.docx
《Android系统匿名共享内存Anonymous Shared MemoryC++调用接口分析.docx》由会员分享,可在线阅读,更多相关《Android系统匿名共享内存Anonymous Shared MemoryC++调用接口分析.docx(35页珍藏版)》请在冰豆网上搜索。
Android系统匿名共享内存AnonymousSharedMemoryC++调用接口分析
Android系统匿名共享内存(AnonymousSharedMemory)C++调用接口分析
在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享数据的机制:
匿名共享内存,它能够辅助内存管理系统来有效地管理内存,它的实现原理我们在前面已经分析过了。
为了方便使用匿名共享内存机制,系统还提供了Java调用接口(MemoryFile)和C++调用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已经分析过了,本文中将继续分析它的C++接口。
在前面一篇文章中,我们分析了匿名共享内存驱动程序Ashmem的实现,重点介绍了它是如何辅助内存管理系统来有效地管理内存的,简单来说,它就是给使用者提供锁机制来辅助管理内存,当我们申请了一大块匿名共享内存时,中间过程有一部分不需要使用时,我们就可以将这一部分内存块解锁,这样内存管理系统就可以把它回收回去了。
接着又在前面一篇文章中,我们分析了匿名共享内存是如何通过Binder进程间通信机制来实现在进程间共享的,简单来说,就是每一个匿名共享内存块都是一个文件,当我们需要在进程间共享时,就把这个文件的打开描述符通过Binder进程间通信机制传递给另一外进程,在传递的过程中,Binder驱动程序就通过这个复制这个打开文件描述符到目标进程中去,从而实现数据共享。
在文章中,我们介绍了如何在Android应用程序中使用匿名共享内存,主要是通过应用程序框架层提供的MemoryFile接口来使用的,而MemoryFile接口是通过JNI方法调用到系统运行时库层中的匿名共享内存C接口,最终通过这些C接口来使用内核空间中的匿名共享内存驱动模块。
为了方便开发者灵活地使用匿名共享内存,Android系统在应用程序框架层中还提供了使用匿名共享内存的C++接口,例如,Android应用程序四大组件之一ContentProvider,它在应用程序间共享数据时,就是通过匿名共享内存机制来实现,但是它并不是通过MemoryFile接口来使用,而是通过调用C++接口中的MemoryBase类和MemoryHeapBase类来使用。
在接下来的内容中,我们就详细分析MemoryHeapBase类和MemoryBase类的实现,以及它们是如何实现在进程间共享数据的。
如果我们想在进程间共享一个完整的匿名共享内存块,可以通过使用MemoryHeapBase接口来实现,如果我们只想在进程间共享一个匿名共享内存块中的其中一部分时,就可以通过MemoryBase接口来实现。
MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,它们都可以作为一个Binder对象来在进程间传输,因此,希望读者在继续阅读本文之前,对Android系统的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类。
从前面一篇文章中,我们知道,继承了RefBase类的子类,它们的对象都可以结合Android系统的智能指针来使用,因此,我们在实例化MemoryHeapBase类时,可以通过智能指针来管理它们的生命周期。
再来看和Binder机制相关的这部分类的逻辑关系。
从这篇文章中,我们知道,所有的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机制相关的这部分类的实现,可以参考一文。
IMemoryBase类主要定义了几个重要的操作匿名共享内存的方法,它定义在frameworks/base/include/binder/IMemory.h文件中:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classIMemoryHeap:
publicIInterface
{
public:
......
virtualintgetHeapID()const=0;
virtualvoid*getBase()const=0;
virtualsize_tgetSize()const=0;
......
};
成员函数getHeapID是用来获得匿名共享内存块的打开文件描述符的;成员函数getBase是用来获得匿名共享内存块的基地址的,有了这个地址之后,我们就可以在程序里面直接访问这块共享内存了;成员函数getSize是用来获得匿名共享内存块的大小的。
MemoryHeapBase类主要用来实现上面IMemoryBase类中列出来的几个成员函数的,这个类声明在frameworks/base/include/binder/MemoryHeapBase.h文件中:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classMemoryHeapBase:
publicvirtualBnMemoryHeap
{
public:
......
/*
*mapsmemoryfromashmem,withthegivennamefordebugging
*/
MemoryHeapBase(size_tsize,uint32_tflags=0,charconst*name=NULL);
......
/*implementIMemoryHeapinterface*/
virtualintgetHeapID()const;
virtualvoid*getBase()const;
virtualsize_tgetSize()const;
......
private:
intmFD;
size_tmSize;
void*mBase;
......
}
MemoryHeapBase类的实现定义在frameworks/base/libs/binder/MemoryHeapBase.cpp文件中,我们先来看一下它的构造函数的实现:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
MemoryHeapBase:
:
MemoryHeapBase(size_tsize,uint32_tflags,charconst*name)
:
mFD(-1),mSize(0),mBase(MAP_FAILED),mFlags(flags),
mDevice(0),mNeedUnmap(false)
{
constsize_tpagesize=getpagesize();
size=((size+pagesize-1)&~(pagesize-1));
intfd=ashmem_create_region(name==NULL?
"MemoryHeapBase":
name,size);
LOGE_IF(fd<0,"errorcreatingashmemregion:
%s",strerror(errno));
if(fd>=0){
if(mapfd(fd,size)==NO_ERROR){
if(flags&READ_ONLY){
ashmem_set_prot_region(fd,PROT_READ);
}
}
}
}
这个构造函数有三个参数,其中size表示要创建的匿名共享内存的大小,flags是用来设置这块匿名共享内存的属性的,例如是可读写的还是只读的,name是用来标识这个匿名共享内存的名字的,可以传空值进来,这个参数只是作为调试信息使用的。
MemoryHeapBase类创建的匿名共享内存是以页为单位的,页的大小一般为4K,但是是可以设置的,这个函数首先通过getpagesize函数获得系统中一页内存的大小值,然后把size参数对齐到页大小去,即如果size不是页大小的整数倍时,就增加它的大小,使得它的值为页大小的整数倍:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
constsize_tpagesize=getpagesize();
size=((size+pagesize-1)&~(pagesize-1));
调整好size的大小后,就调用系统运行时库层的C接口ashmem_create_region来创建一块共享内存了:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
intfd=ashmem_create_region(name==NULL?
"MemoryHeapBase":
name,size);
这个函数我们在前面一篇文章中可以介绍过了,这里不再详细,它只要就是通过Ashmem驱动程序来创建一个匿名共享内存文件,因此,它的返回值是一个文件描述符。
得到了这个匿名共享内存的文件描述符后,还需要调用mapfd成函数把它映射到进程地址空间去:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
status_tMemoryHeapBase:
:
mapfd(intfd,size_tsize,uint32_toffset)
{
......
if((mFlags&DONT_MAP_LOCALLY)==0){
void*base=(uint8_t*)mmap(0,size,
PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
......
mBase=base;
......
}else{
......
}
mFD=fd;
mSize=size;
returnNO_ERROR;
}
一般我们创建MemoryHeapBase类的实例时,都是需要把匿名共享内存映射到本进程的地址空间去的,因此,这里的条件(mFlags&DONT_MAP_LOCALLY==0)为true,于是执行系统调用mmap来执行内存映射的操作。
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
void*base=(uint8_t*)mmap(0,size,
PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
传进去的第一个参数0表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置,第二个参数size表示要映射的匿名共享内文件的大小,第三个参数PROT_READ|PROT_WRITE表示这个匿名共享内存是可读写的,第四个参数fd指定要映射的匿名共享内存的文件描述符,第五个参数offset表示要从这个文件的哪个偏移位置开始映射。
调用了这个函数之后,最后会进入到内核空间的ashmem驱动程序模块中去执行ashmem_map函数,这个函数的实现具体可以参考一文,这里就不同详细描述了。
调用mmap函数返回之后,就得这块匿名共享内存在本进程地址空间中的起始访问地址了,将这个地址保存在成员变量mBase中,最后,还将这个匿名共享内存的文件描述符和以及大小分别保存在成员变量mFD和mSize中。
回到前面MemoryHeapBase类的构造函数中,将匿名共享内存映射到本进程的地址空间去后,还看继续设置这块匿名共享内存的读写属性:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
if(fd>=0){
if(mapfd(fd,size)==NO_ERROR){
if(flags&READ_ONLY){
ashmem_set_prot_region(fd,PROT_READ);
}
}
}
上面调用mapfd函数来映射匿名共享内存时,指定这块内存是可读写的,但是如果传进来的参数flags设置了只读属性,那么还需要调用系统运行时库存层的ashmem_set_prot_region函数来设置这块匿名共享内存为只读,这个函数定义在system/core/libcutils/ashmem-dev.c文件,有兴趣的读者可以自己去研究一下。
这样,通过这个构造函数,一块匿名共享内存就建立好了,其余的三个成员函数getHeapID、getBase和getSize就简单了:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
intMemoryHeapBase:
:
getHeapID()const{
returnmFD;
}
void*MemoryHeapBase:
:
getBase()const{
returnmBase;
}
size_tMemoryHeapBase:
:
getSize()const{
returnmSize;
}
接下来我们再来看一下MemoryHeapBase在Client端实现的类图:
这个类图中的类也是可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括BpMemoryHeap、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BpInterface、BpRefBase、IBinder、BpBinder、ProcessState和IPCThreadState七个类。
在和匿名共享内存操作相关的类中,BpMemoryHeap类是前面分析的MemoryHeapBase类在Client端进程的远接接口类,当Client端进程从ServiceManager或者其它途径获得了一个MemoryHeapBase对象的引用之后,就会在本地创建一个BpMemoryHeap对象来代表这个引用。
BpMemoryHeap类同样是要实现IMemoryHeap接口,同时,它是从RefBase类继承下来的,因此,它可以与智能指针来结合使用。
在和Binder机制相关的类中,和Server端实现不一样的地方是,Client端不需要实现BnInterface和BBinder两个类,但是需要实现BpInterface、BpRefBase和BpBinder三个类。
BpInterface类继承于BpRefBase类,而在BpRefBase类里面,有一个成员变量mRemote,它指向一个BpBinder对象,当BpMemoryHeap类需要向Server端对象发出请求时,它就会通过这个BpBinder对象的transact函数来发出这个请求。
这里的BpBinder对象是如何知道要向哪个Server对象发出请深圳市的呢?
它里面有一个成员变量mHandle,它表示的是一个Server端Binder对象的引用值,BpBinder对象就是要通过这个引用值来把请求发送到相应的Server端对象去的了,这个引用值与Server端Binder对象的对应关系是在Binder驱动程序内部维护的。
这里的ProcessSate类和IPCThreadState类的作用和在Server端的作用是类似的,它们都是负责和底层的Binder驱动程序进行交互,例如,BpBinder对象的transact函数就通过线程中的IPCThreadState对象来将Client端请求发送出去的。
这些实现具体可以参考一文。
这里我们主要关注BpMemoryHeap类是如何实现IMemoryHeap接口的,这个类声明和定义在frameworks/base/libs/binder/IMemory.cpp文件中:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classBpMemoryHeap:
publicBpInterface
{
public:
BpMemoryHeap(constsp
......
virtualintgetHeapID()const;
virtualvoid*getBase()const;
virtualsize_tgetSize()const;
......
private:
mutablevolatileint32_tmHeapId;
mutablevoid*mBase;
mutablesize_tmSize;
......
}
先来看构造函数BpMemoryHeap的实现:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
BpMemoryHeap:
:
BpMemoryHeap(constsp
:
BpInterface
mHeapId(-1),mBase(MAP_FAILED),mSize(0),mFlags(0),mRealHeap(false)
{
}
它的实现很简单,只是初始化一下各个成员变量,例如,表示匿名共享内存文件描述符的mHeapId值初化为-1、表示匿名内共享内存基地址的mBase值初始化为MAP_FAILED以及表示匿名共享内存大小的mSize初始为为0,它们都表示在Client端进程中,这个匿名共享内存还未准备就绪,要等到第一次使用时才会去创建。
这里还需要注意的一点,参数impl指向的是一个BpBinder对象,它里面包含了一个指向Server端Binder对象,即MemoryHeapBase对象的引用。
其余三个成员函数getHeapID、getBase和getSize的实现是类似的:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
intBpMemoryHeap:
:
getHeapID()const{
assertMapped();
returnmHeapId;
}
void*BpMemoryHeap:
:
getBase()const{
assertMapped();
returnmBase;
}
size_tBpMemoryHeap:
:
getSize()const{
assertMapped();
returnmSize;
}
即它们在使用之前,都会首先调用assertMapped函数来保证在Client端的匿名共享内存是已经准备就绪了的:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidBpMemoryHeap:
:
assertMapped()const
{
if(mHeapId==-1){
sp
sp
heap->assertReallyMapped();
if(heap->mBase!
=MAP_FAILED){
Mutex:
:
Autolock_l(mLock);
if(mHeapId==-1){
mBase=heap->mBase;
mSize=heap->mSize;
android_atomic_write(dup(heap->mHeapId),&mHeapId);
}
}e
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android系统匿名共享内存Anonymous Shared MemoryC+调用接口分析 Android 系统 匿名 共享 内存 Anonymous MemoryC 调用 接口 分析
链接地址:https://www.bdocx.com/doc/4312763.html