IMemory,IMemoryHeap都是在binder上使用的,用于进程间内存共享的操作,用于在binder上传输大内存块的操作。因为parcel提供的共享内存块大小有限,并来回地复制,效率太低。故引入这两个接口来高效实现内存共享(传输)
1.IMemory public IInterface
表示一个内存块,并提供了访问这些内存块的基本操作,如pointer() size() 等
android里IMemory的实现并不实际分配内存,内存都是由IMemoryHeap的实现类来分配的。IMemory的实际内存指向了IMemoryHeap的实现类。IMemory可以看成是一个对IMemoryHeap的adapter模式,让IMemoryHeap更容易用。
2.IMemoryHeap public IInterface
表示一个内存块,一般来说其实现类为
MemoryHeapBase 表示的是 Ashmem
的一个包装实现。Ashmem是android一个驱动,主要用于进程间的内存共享
而设计,它可以记录内存和进程间的引用次数,但分配的内存在物理上不是连续的
MemoryHeapPmem 对pmem驱动的一个包装实现,pmem是android的一个驱动,提供连接物理内存的进程间共享操
作,它和MemoryHeapBase最大的区别在于分配的内存是连接的,这个用于dma会方面些。
3. 如何在进程间共享内存?
具体实现在
BpMemoryHeap,BnMemoryHeap上.
其中BpMemoryHeap 是客户端(proxy class)的使用,
BnMemoryHeap 是服务端的使用
BpMemoryHeap
可参考IMemory.cpp的实现,特别是对BpMemoryHeap的实现上
3.1
BnMemoryHeap 提供 HEAP_ID ,这个功能把 在BnMemoryHeap上打开的 Ashmem或者pmem的
设备fd 通过binder的writeFileDescriptor写给客户端
代码如下:
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()); //将pmem,或者ashmem的fd写给客户端
reply->writeInt32(getSize());
reply->writeInt32(getFlags());
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
3.2 BpMemoryHeap 的实现中,会对从BnMemoryHeap
传回来的id做处理,dup(remotefd)用mmap再次打开,以实现真正的进程间内存共享
void* BpMemoryHeap::getBase() const {
assertMapped(); //处理进程间的内存map
return
mBase;
}
void BpMemoryHeap::assertMapped() const
{
if (mHeapId
== -1) {
sp
binder(const_cast(this)->asBinder());
sp
heap(static_cast(find_heap(binder).get()));
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 );
}
} else {
// something went wrong
free_heap(binder);
}
}
}
void BpMemoryHeap::assertReallyMapped() const
{
if (mHeapId
== -1) {
// remote call without mLock held, worse case scenario, we end
up
// calling transact() from multiple threads, but that's not a
problem,
// only mmap below must be in the critical section.
Parcel data, reply;
data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
status_t err = remote()->transact(HEAP_ID, data,
&reply);
int parcel_fd =
reply.readFileDescriptor();//从binder里取回BnMemoryHeap写回的fd
ssize_t size = reply.readInt32();
uint32_t flags = reply.readInt32();
LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d
(%s)",
asBinder().get(), parcel_fd, size, err, strerror(-err));
int fd = dup( parcel_fd );//dup一个,为什么这么做?parcel类里有注解
LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
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,
0);//真的做mmap了,终于实现了内存共享
if (mBase == MAP_FAILED) {
LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d
(%s)",
asBinder().get(), size, fd, strerror(errno));
close(fd);
} else {
mSize = size;
mFlags = flags;
android_atomic_write(fd, &mHeapId);
}
}
}
}
看来android上对内存共享上效率的考虑还是做的很充分的。