Android 匿名共享内存C++接口分析
原址
在上一篇Android 匿名共享内存C接口分析中介绍了Android系统的匿名共享内存C语言访问接口,本文在前文的基础上继续介绍Android系统的匿名共享内存提供的C++访问接口。在C++层通过引入Binder进程间通信机制可以实现跨进程访问匿名共享内存。我们知道Android匿名共享内存的设计本身就是为了实现进程间共享大量数据,当源进程开辟一块匿名共享内存并把这块匿名共享内存映射到当前进程的虚拟地址空间从而使当前进程可以直接访问这块匿名共享内存后,如何让目标进程共享访问这块匿名共享内存呢?这就需要利用Binder进程间通信方式将匿名共享内存的文件描述符传输给目标进程,目标进程才能将得到的匿名共享内存文件描述符映射到本进程地址空间中,只有这样目标进程才能访问源进程创建的这块匿名共享内存。将Binder进程间通信方式与匿名共享内存相结合实现小数据量传输换来大数据量共享。
由于这里用到了Binder进程间通信机制,这里再次贴上Android系统的Binder通信设计框架,关于Binder通信的具体分析,请查看Binder通信模块中的一系列文章。
MemoryHeapBase
Android使用MemoryHeapBase接口来实现进程间共享一个完整的匿名共享内存块,通过MemoryBase接口来实现进程间共享一个匿名共享内存块中的其中一部分,MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,都可以作为一个Binder对象来在进程间传输。首先介绍MemoryHeapBase如何实现一个完整匿名共享内存块的共享,下面是MemoryHeapBase在Binder通信框架中的类关系图:
既然Android的匿名共享内存涉及到了Binder通信机制,那就必定包括客户端和服务端两种实现方式。图中黄色标识的类及MemoryHeapBase是匿名共享内存在服务进程中的实现类,而粉红色标识的类则是匿名共享内存在Client进程中的引用类。IMemoryHeapding接口定义了匿名共享内存访问接口;BpMemoryHeap定义了匿名共享内存在客户进程中的业务逻辑,BpInterface,BpRefBase,BpBinder则是为业务逻辑层的BpMemoryHeap提供进程间通信支持,ProcessState和IPCThreadState是Binder进程和线程的抽象操作类,为BpBinder和Binder驱动交互提供辅助接口。黄色标识类BBinder,BnInterface是服务进程中用于支持Binder通信实现类,BnMemoryHeap用于分发关于匿名共享内存请求命令,而MemoryHeapBase实现了匿名共享内存在服务进程的所有业务逻辑。了解了整个匿名共享内存的框架设计后,接下来根据代码具体分析Android是如何在进程间共享匿名共享内存的。在IMemoryBase类中定义了匿名共享内存的访问接口:
- class IMemoryHeap : public IInterface
- {
- public:
- DECLARE_META_INTERFACE(MemoryHeap);
- ...
- //用来获得匿名共享内存块的打开文件描述符
- virtual int getHeapID() const = 0;
- //用来获得匿名共享内存块的基地址
- virtual void* getBase() const = 0;
- //用来获得匿名共享内存块的大小
- virtual size_t getSize() const = 0;
- //用来获得匿名共享内存块的保护标识位
- virtual uint32_t getFlags() const = 0;
- //用来获得匿名共享内存块中的偏移量
- virtual uint32_t getOffset() const = 0;
- ...
- };
Server端
MemoryHeapBase类实现了IMemoryBase提供的接口函数,位于frameworks\native\libs\binder\MemoryBase.cpp,MemoryHeapBase提供了四种构造函数:
- MemoryHeapBase::MemoryHeapBase()
- : mFD(-1), mSize(0), mBase(MAP_FAILED),
- mDevice(NULL), mNeedUnmap(false), mOffset(0)
- {
- }
size表示要创建的匿名共享内存的大小,flags是用来设置这块匿名共享内存的属性的,name是用来标识这个匿名共享内存的名字,mFD表示匿名共享内存的文件描述符,mBase标识匿名共享内存的基地址,mDevice表示匿名共享内存的设备文件。这个构造函数只是简单初始化了各个成员变量。
- MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false), mOffset(0)
- {
- //获得系统中一页大小的内存
- const size_t pagesize = getpagesize();
- //内存页对齐
- size = ((size + pagesize-1) & ~(pagesize-1));
- //创建一块匿名共享内存
- int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
- ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
- if (fd >= 0) {
- //将创建的匿名共享内存映射到当前进程地址空间中
- if (mapfd(fd, size) == NO_ERROR) {
- if (flags & READ_ONLY) {//如果地址映射成功,修改匿名共享内存的访问属性
- ashmem_set_prot_region(fd, PROT_READ);
- }
- }
- }
- }
在以上构造函数中根据参数,利用匿名共享内存提供的C语言接口创建一块匿名共享内存,并映射到当前进程的虚拟地址空间,参数size是指定匿名共享内存的大小,flags指定匿名共享内存的访问属性,name指定匿名共享内存的名称,如果没有指定名称,默认命名为MemoryHeapBase。
- MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false), mOffset(0)
- {
- int open_flags = O_RDWR;
- if (flags & NO_CACHING)
- open_flags |= O_SYNC;
- //打开匿名共享内存设备文件
- int fd = open(device, open_flags);
- ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
- if (fd >= 0) {
- //将指定的匿名共享内存大小按页对齐
- const size_t pagesize = getpagesize();
- size = ((size + pagesize-1) & ~(pagesize-1));
- //将匿名共享内存映射到当前进程地址空间
- if (mapfd(fd, size) == NO_ERROR) {
- mDevice = device;
- }
- }
- }
该构造函数通过指定已创建的匿名共享内存的设备文件来打开该共享内存,并映射到当前进程地址空间。device指定已经创建的匿名共享内存的设备文件路径,size指定匿名共享内存的大小。
- MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false), mOffset(0)
- {
- //指定匿名共享内存大小按页对齐
- const size_t pagesize = getpagesize();
- size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(dup(fd), size, offset);
- }
以上四种构造函数都通过mapfd函数来将匿名共享内存映射到当前进程的虚拟地址空间,以便进程可以直接访问匿名共享内存中的内容。
- status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
- {
- if (size == 0) {
- // try to figure out the size automatically
- #ifdef HAVE_ANDROID_OS
- // first try the PMEM ioctl
- pmem_region reg;
- int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®);
- if (err == 0)
- size = reg.len;
- #endif
- if (size == 0) { // try fstat
- struct stat sb;
- if (fstat(fd, &sb) == 0)
- size = sb.st_size;
- }
- // if it didn't work, let mmap() fail.
- }
- if ((mFlags & DONT_MAP_LOCALLY) == 0) {
- //通过mmap系统调用进入内核空间的匿名共享内存驱动,并调用ashmem_map函数将匿名共享内存映射到当前进程
- void* base = (uint8_t*)mmap(0, size,PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
- if (base == MAP_FAILED) {
- ALOGE("mmap(fd=%d, size=%u) failed (%s)",fd, uint32_t(size), strerror(errno));
- close(fd);
- return -errno;
- }
- mBase = base;
- mNeedUnmap = true;
- } else {
- mBase = 0; // not MAP_FAILED
- mNeedUnmap = false;
- }
- mFD = fd;
- mSize = size;
- mOffset = offset;
- return NO_ERROR;
- }
mmap函数的第一个参数0表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置,第二个参数size表示要映射的匿名共享内文件的大小,第三个参数PROT_READ|PROT_WRITE表示这个匿名共享内存是可读写的,第四个参数fd指定要映射的匿名共享内存的文件描述符,第五个参数offset表示要从这个文件的哪个偏移位置开始映射。调用了这个函数之后,最后会进入到内核空间的ashmem驱动程序模块中去执行ashmem_map函数,调用mmap函数返回之后,就得这块匿名共享内存在本进程地址空间中的起始访问地址了,将这个地址保存在成员变量mBase中,最后,还将这个匿名共享内存的文件描述符和以及大小分别保存在成员变量mFD和mSize,并提供了相应接口函数来访问这些变量值。通过构造MemoryHeapBase对象就可以创建一块匿名共享内存,或者映射一块已经创建的匿名共享内存到当前进程的地址空间。
- int MemoryHeapBase::getHeapID() const {
- return mFD;
- }
- void* MemoryHeapBase::getBase() const {
- return mBase;
- }
- size_t MemoryHeapBase::getSize() const {
- return mSize;
- }
- uint32_t MemoryHeapBase::getFlags() const {
- return mFlags;
- }
- const char* MemoryHeapBase::getDevice() const {
- return mDevice;
- }
- uint32_t MemoryHeapBase::getOffset() const {
- return mOffset;
- }
Client端
- BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
- : BpInterface<IMemoryHeap>(impl),
- mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)
- {
- }
在BpMemoryHeap的构造函数里只是初始化一些成员变量,BpMemoryHeap提供了和服务端MemoryHeapBase相对应的服务函数:
- int BpMemoryHeap::getHeapID() const {
- assertMapped();
- return mHeapId;
- }
- void* BpMemoryHeap::getBase() const {
- assertMapped();
- return mBase;
- }
- size_t BpMemoryHeap::getSize() const {
- assertMapped();
- return mSize;
- }
- uint32_t BpMemoryHeap::getFlags() const {
- assertMapped();
- return mFlags;
- }
- uint32_t BpMemoryHeap::getOffset() const {
- assertMapped();
- return mOffset;
- }
通过BpMemoryHeap代理对象访问匿名共享内存前都会调用函数assertMapped()来判断是否已经向服务获取到匿名共享内存的信息,如果没有就向服务端发起请求:
- void BpMemoryHeap::assertMapped() const
- {
- if (mHeapId == -1) {//如果还没有请求服务创建匿名共享内存
- //将当前BpMemoryHeap对象转换为IBinder对象
- sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- //从成员变量gHeapCache中查找对应的BpMemoryHeap对象
- sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- //向服务端请求获取匿名共享内存信息
- heap->assertReallyMapped();
- //判断该匿名共享内存是否映射成功
- if (heap->mBase != MAP_FAILED) {
- Mutex::Autolock _l(mLock);
- //保存服务端返回回来的匿名共享内存信息
- if (mHeapId == -1) {
- mBase = heap->mBase;
- mSize = heap->mSize;
- mOffset = heap->mOffset;
- android_atomic_write( dup( heap->mHeapId ), &mHeapId );
- }
- } else {
- // something went wrong
- free_heap(binder);
- }
- }
- }
mHeapId等于-1,表示匿名共享内存还为准备就绪,因此请求服务端MemoryHeapBase创建匿名共享内存,否则该函数不作任何处理。只有第一次使用匿名共享时才会请求服务端创建匿名共享内存。由于在客户端进程中使用同一个BpBinder代理对象可以创建多个与匿名共享内存业务相关的BpMemoryHeap对象,因此定义了类型为HeapCache的全局变量gHeapCache用来保存创建的所有BpMemoryHeap对象,assertMapped函数首先将当前BpMemoryHeap对象强制转换为IBinder类型对象,然后调用find_heap()函数从全局变量gHeapCache中查找出对应的BpMemoryHeap对象,并调用assertReallyMapped()函数向服务进程的BnemoryHeap请求创建匿名共享内存。
- void BpMemoryHeap::assertReallyMapped() const
- {
- if (mHeapId == -1) {//再次判断是否已经请求创建过匿名共享内存
- Parcel data, reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- //向服务端BnMemoryHeap发起请求
- status_t err = remote()->transact(HEAP_ID, data, &reply);
- int parcel_fd = reply.readFileDescriptor();
- ssize_t size = reply.readInt32();
- uint32_t flags = reply.readInt32();
- uint32_t offset = reply.readInt32();
- //保存服务进程创建的匿名共享内存的文件描述符
- int fd = dup( parcel_fd );
- int access = PROT_READ;
- if (!(flags & READ_ONLY)) {
- access |= PROT_WRITE;
- }
- Mutex::Autolock _l(mLock);
- if (mHeapId == -1) {
- //将服务进程创建的匿名共享内存映射到当前客户进程的地址空间中
- mRealHeap = true;
- mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
- if (mBase == MAP_FAILED) {
- ALOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)",asBinder().get(), size, fd, strerror(errno));
- close(fd);
- } else {//映射成功后,将匿名共享内存信息保存到BpMemoryHeap的成员变量中,供其他接口函数访问
- mSize = size;
- mFlags = flags;
- mOffset = offset;
- android_atomic_write(fd, &mHeapId);
- }
- }
- }
- }
该函数首先通过Binder通信方式向服务进程请求创建匿名共享内存,当服务端BnMemoryHeap对象创建完匿名共享内存后,并将共享内存信息返回到客户进程后,客户进程通过系统调用mmap函数将匿名共享内存映射到当前进程的地址空间,这样客户进程就可以访问服务进程创建的匿名共享内存了。当了解Binder通信机制,就知道BpMemoryHeap对象通过transact函数向服务端发起请求后,服务端的BnMemoryHeap的onTransact函数会被调用。
- status_t BnMemoryHeap::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
- {
- switch(code) {
- case HEAP_ID: {
- CHECK_INTERFACE(IMemoryHeap, data, reply);
- reply->writeFileDescriptor(getHeapID());
- reply->writeInt32(getSize());
- reply->writeInt32(getFlags());
- reply->writeInt32(getOffset());
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
服务端的BnMemoryHeap对象将调用服务端的匿名共享内存访问接口得到创建的匿名共享内存信息,并返回给客户端进程,MemoryHeapBase是BnMemoryHeap的子类,实现了服务端的匿名共享内存访问接口。全局变量gHeapCache用来保存客户端创建的所有BpMemoryHeap对象,它的类型为HeapCache
MemoryBase
- class IMemory : public IInterface
- {
- public:
- DECLARE_META_INTERFACE(Memory);
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
- // helpers
- void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
- void* pointer() const;
- size_t size() const;
- ssize_t offset() const;
- };
在IMemory类中实现了大部分接口函数,只有getMemory函数是在IMemory的子类MemoryBase中实现的。成员函数getMemory用来获取内部的MemoryHeapBase对象的IMemoryHeap接口。
- void* IMemory::pointer() const {
- ssize_t offset;
- sp<IMemoryHeap> heap = getMemory(&offset);
- void* const base = heap!=0 ? heap->base() : MAP_FAILED;
- if (base == MAP_FAILED)
- return 0;
- return static_cast<char*>(base) + offset;
- }
- size_t IMemory::size() const {
- size_t size;
- getMemory(NULL, &size);
- return size;
- }
- ssize_t IMemory::offset() const {
- ssize_t offset;
- getMemory(&offset);
- return offset;
- }
客户端
- sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
- {
- if (mHeap == 0) {//指向的匿名共享内存MemoryHeapBase为空
- Parcel data, reply;
- data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
- //向服务端MemoryBase发起RPC请求
- if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
- //读取服务端返回来的结果
- sp<IBinder> heap = reply.readStrongBinder();//读取匿名共享内存MemoryHeapBase的IBinder对象
- ssize_t o = reply.readInt32();//读取匿名共享内存中的偏移量
- size_t s = reply.readInt32();//读取匿名共享内存的大小
- //如果服务端返回来的用于描述整块匿名共享内存的MemoryHeapBase不为空
- if (heap != 0) {
- mHeap = interface_cast<IMemoryHeap>(heap);
- if (mHeap != 0) {//将匿名共享内存的偏移和大小保存到成员变量中
- mOffset = o;
- mSize = s;
- }
- }
- }
- }
- //将成员变量赋值给传进来的参数,从而修改参数值
- if (offset) *offset = mOffset;
- if (size) *size = mSize;
- return mHeap;
- }
服务端
- status_t BnMemory::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
- {
- switch(code) {
- case GET_MEMORY: {
- //根据客户端发送过来的接口描述进行检查确认
- CHECK_INTERFACE(IMemory, data, reply);
- ssize_t offset;
- size_t size;
- //调用服务端的getMemory函数获取匿名共享内存对象MemoryHeapBase及匿名共享内存大小,偏移,并返回给客户端
- reply->writeStrongBinder(getMemory(&offset, &size)->asBinder() );
- //将偏移量返回给客户端
- reply->writeInt32(offset);
- //将匿名共享内存大小返回给客户端
- reply->writeInt32(size);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
服务端的getMemory函数由BnMemory的子类MemoryBase实现
- sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
- {
- if (offset) *offset = mOffset;
- if (size) *size = mSize;
- return mHeap;
- }
这里只是将成员变量的值赋给传进来的参数,从而修改参数值,由于参数类型为指针类型,因此形参的改变必然改变实参。那服务端MemoryBase的成员变量mSize,mOffset,mHeap是在什么时候赋值的呢?MemoryBase的构造函数如下:
- MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
- ssize_t offset, size_t size)
- : mSize(size), mOffset(offset), mHeap(heap)
- {
- }
从MemoryBase的构造函数可以看出,它的成员变量值都是在构造MemoryBase对象是被初始化的。参数heap指向的是一个MemoryHeapBase对象,真正的匿名共享内存就是由它来维护的,参数offset表示这个MemoryBase对象所要维护的这部分匿名共享内存在整个匿名共享内存块中的起始位置,参数size表示这个MemoryBase对象所要维护的这部分匿名共享内存的大小。MemoryBase用于共享匿名共享内存中的部分内存,在构造MemoryBase对象是指定共享的整块匿名共享内存为mHeap,大小为mSize,被共享的内存在整块匿名共享内存中的偏移量为mOffset。客户端通过Binder进程间通信方式获取用于描述部分共享内存的MemoryBase对象信息,MemoryBase只是用来维护部分匿名共享内存,而匿名共享内存的创建依然是通过MemoryHeapBase来完成的。
Android 匿名共享内存C++接口分析相关推荐
- Android 匿名共享内存Java接口分析
原址 在Android 匿名共享内存驱动源码分析中介绍了匿名共享内存的驱动实现过程,本文在Android匿名共享内存驱动基础上,介绍Android匿名共享内存对外Android系统的匿名共享内存子系统 ...
- Android 匿名共享内存C接口分析
原址 在Android 匿名共享内存驱动源码分析中详细分析了匿名共享内存在Linux内核空间的实现,虽然内核空间实现了匿名共享内存,但仍然需要在用户空间为用户使用匿名共享内存提供访问接口.Androi ...
- Anonymous Shared Memory android 匿名共享内存
原址 两个特点: 能够辅助内存管理系统来有效的管理不再使用的内存块 通过Binder进程间通信机制来实现进程间的内存共享. Android系统的匿名共享内存子系统的主体是以驱动程序的形式实现在内核空间 ...
- Android 匿名共享内存驱动源码分析
原址 Android系统的匿名共享内存Ashmem驱动程序利用了Linux的共享内存子系统导出的接口来实现,本文通过源码分析方式详细介绍Android系统的匿名共享内存机制.在Android系统中,匿 ...
- 安卓ashmem(匿名共享内存映射)学习native篇
转自:http://blog.csdn.net/u010657219/article/details/41248965 先看看之前三个进程的关系: 由于这里用到了Binder进程间通信机制,这里再次贴 ...
- Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(6)
接下来,我们再来看看server模块的实现.在external/ashmem/common目录下,只有一个源文件SharedBufferServer.cpp,它实现了内存共享服务SharedBuffe ...
- Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析
出自:http://blog.csdn.net/luoshengyang/article/details/6939890 在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享 ...
- Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(7)
同样,我们需要为这个Client端应用程序编译一个编译脚本,在external/ashmem/client目录下,新建一个Android.mk文件,它的内容如下所示: LOCAL_ ...
- Android系统的Ashmem匿名共享内存子系统分析(4)- Ashmem子系统的 Java访问接口
声明 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法,记得2019年6.7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾- ...
最新文章
- 【AI】caffe使用步骤(三):编写求解文件solver.prototxt
- 几个不错的自己到的少的游戏站
- 从零写一个编译器(四):语法分析之构造有限状态自动机
- spring boot配置dubbo注意事项
- Shell:Lite OS在线调试工具知多少
- 吴恩达神经网络和深度学习-学习笔记-9-mini-batch梯度下降法
- 10分钟搞定工作周报
- win10升级后 IE 火狐 谷歌浏览器 不能上网 解决办法
- 交换芯片架构 (一)
- python 绘制三维图
- FHIR标准和国际基于FHIR的互联互通实践(7):国际互联互通实践
- JavaScript 笔记之数组
- JavaScript 中的内存和性能、模拟事件(读书笔记思维导图)
- Win10系统pin码忘记了?
- 计算识别率的matlab代码,基于MATLAB神经网络图像识别的高识别率代码
- 净误差与遗漏为负值的含义_为什么遗漏与误差项为负反映了去向不明的资本外逃?...
- sim900芯片—GPRS模块ppp拨号上网
- 动物园IP网络广播-基于IP局域网络的动物园背景音乐广播系统-动物园智能广播系统设计指南
- 记录python下载的第三方安装包
- (四)Substance Painter贴图制作软件的使用
热门文章
- iOS Crash常规跟踪方法及Bugly集成运用
- C++三大特性之多态
- CentOS 6.6 安装 Node.js
- linux 查找并删除
- Zookeeper脑图
- Web 前端开发框架收集
- yue-library是一个基于SpringBoot封装的基础库
- Warning:Null pointer access: The variable addStrings can only be null at this location
- mybatis 原理_了解Mybatis的工作原理吗
- 一个按钮控制暂停和开始java_《第一炉香》|一个女人的自甘堕落,从控制不住欲望开始...