Ashmem(anonymous shared memory)是Android中的匿名共享内存机制,
可以从内核级别提供两个进程之间的数据共享。
声明: /system/core/include/cutils/ashmem.h
定义实现: /system/core/libcutils/ashmem-dev.c
原型:
/*
ashmem属于misc设备的一个伪文件,虽然是匿名共享内存, 但是也可以指定一个名字
返回值:
返回代表匿名共享内存的文件描述符, 如果返回0则代表出错了。
*/
int ashmem_create_region(
const char *name, //共享内存的名字(可选, 传NULL也可以)
size_t size // 要申请的共享内存的大小
)
比如: 设置访问(读写)权限等
原型
/*
参数说明:
prot : 要设置的权限保护位
- PROT_EXEC: 可执行权限
- PROT_READ :可读权限
- PROT_WRITE :可写权限
- PROT_SEM: 可用于原子操作
- PROT_GROWSDOWN ??
- PROT_GROWSUP: ??
- PROT_NONE : 不给权限
*/
int ashmem_set_prot_region(
int fd, // ashmem的代表fd(ashmem_create_region的返回值)
int prot // 要设置的权限保护位
)
原型:
/*
返回值:
<0: 异常, 并设置errno
>0: 返回内存大小, 同时可以确定该fd确实引用到了该ashmem区域.
*/
int ashmem_get_size_region(
int fd //代表ashmem内存的文件描述符(ashmem_create_region的返回值)
)
原型:
/*
标记那些内存正在使用, 共享内存在创建之初都是pinned状态的.
并且只有unpinned(调用unpin)状态的内存段才可以被pin.
*/
int ashmem_pin_region(
int fd, //代表ashmem内存的文件描述符(ashmem_create_region的返回值)
size_t offset, //锁定开始位置的偏移量
size_t len //要锁定的的区域长度(从offset开始)
)
原型:
/*
标记那些内存不再使用了, 只有pinned状态的内存段才可以被unpin
*/
int ashmem_unpin_region(
int fd, //代表ashmem内存的文件描述符(ashmem_create_region的返回值)
size_t offset, //需要解锁的内存段的开始位置的偏移量
size_t len //要解锁的的区域长度(从offset开始)
)
Ashmem只是用来开辟一块共享内存空间, 如果要实现进程间共享, 还需要使用借助mmap来将申请的ashmem区域映射到进程中。
注:图片来自文章 Android匿名共享内存(Ashmem)原理
基于Ashmem机制封装的内存共享接口。
用于封装对ashmem的内存申请和映射功能。
/frameworks/native/include/binder/MemoryHeapBase.h
//MemoryHeapBase 是继承自BnMemoryHeap的, 所以可以说是属于binder通信的服务端角色。
class MemoryHeapBase : public virtual BnMemoryHeap
{
public:
enum {
READ_ONLY = IMemoryHeap::READ_ONLY,
// memory won't be mapped locally, but will be mapped in the remote
// process.
DONT_MAP_LOCALLY = 0x00000100,
NO_CACHING = 0x00000200
};
/*
* maps the memory referenced by fd. but DOESN'T take ownership
* of the filedescriptor (it makes a copy with dup()
*/
MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);
/*
* maps memory from the given device(设备是指文件路径)
*/
MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);
/*
* 从ashmem来映射内存, 可以给定一个名字,用来debug
* maps memory from ashmem, with the given name for debugging
*/
MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);
virtual ~MemoryHeapBase();
/* implement IMemoryHeap interface */
virtual int getHeapID() const; //内存块的引用id, 就是文件描述符mFD
/* virtual address of the heap. returns MAP_FAILED in case of error */
virtual void* getBase() const;
virtual size_t getSize() const;
virtual uint32_t getFlags() const;
virtual uint32_t getOffset() const;
const char* getDevice() const;
/* this closes this heap -- use carefully */
void dispose();
/* this is only needed as a workaround, use only if you know
* what you are doing */
status_t setDevice(const char* device) {
if (mDevice == 0)
mDevice = device;
return mDevice ? NO_ERROR : ALREADY_EXISTS;
}
protected:
MemoryHeapBase();
// init() takes ownership of fd
status_t init(int fd, void *base, int size,
int flags = 0, const char* device = NULL);
private:
//使用mmap将创建的内存通过fd映射到进程中。
status_t mapfd(int fd, size_t size, uint32_t offset = 0);
int mFD; //内存Id
size_t mSize; //内存大小
void* mBase; //内存首地址
uint32_t mFlags;
const char* mDevice;
bool mNeedUnmap;
uint32_t mOffset; //偏移量
};
主要的构造:
//使用Ashmem创建匿名共享内存, 并获取内存代表的文件描述符FD。
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)); //计算申请的内存大小
//使用ashmem_create_region申请内存
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);
}
}
}
}
//通过获取的文件描述符,使用mmap将FD代表的内存信息,真正的映射物理内存到本进程。
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
{
if (size == 0) {
// try to figure out the size automatically
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创建的内存映射到本地进程中
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;
}
//ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
mBase = base;
mNeedUnmap = true;
} else {
mBase = 0; // not MAP_FAILED
mNeedUnmap = false;
}
//记录映射所需要的信息,在其他进程中作为映射参数。
mFD = fd; //记录fd(HeapId)
mSize = size; //记录内存大小
mOffset = offset; //记录偏移量
return NO_ERROR;
}
/*获取内存的代表fd*/
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;
}
MemoryHeapBase是作为申请内存的代表,而要将申请的内存用于多个进程来进行共享, 一般使用MemoryBase来携带MemoryHeapBase, 而进程间进行通信的代表, 是MemoryBase的父类 - IMemory。
真正的操作接口其实就是在IMemory中, MemoryBase只是作为数据记录。
///frameworks/native/include/binder/MemoryBase.h
//同样的MemoryBase,也扮演这个服务端(申请内存的一端)的角色。
class MemoryBase : public BnMemory
{
public:
MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
virtual ~MemoryBase();
virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
protected:
size_t getSize() const { return mSize; }
ssize_t getOffset() const { return mOffset; }
const sp<IMemoryHeap>& getHeap() const { return mHeap; }
private:
size_t mSize;
ssize_t mOffset;
sp<IMemoryHeap> mHeap;
};
///frameworks/native/libs/binder/MemoryBase.cpp
//记录MemoryHeapBase的参数,即在使用MemoryHeapBase申请匿名内存的时候的参数:
// - 内存信息(包括描述符fd(heapId),首地址等 )
// - 偏移量信息
// - 内存大小信息
MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
ssize_t offset, size_t size)
: mSize(size), mOffset(offset), mHeap(heap)
{
}
//获取IMemoryHeap(MemoryHeapBase的binder通信接口), offset 和 size参数。
sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
{
if (offset) *offset = mOffset;
if (size) *size = mSize;
return mHeap;
}
MemoryBase::~MemoryBase()
{
}
说明:
1. 服务端通过MemoryHeapBase申请到共享内存;
2. 通过MemoryBase包装MemoryHeapBase对象,保存共享内存的信息数据;
3. 通过Binder通信, 将IMemory携带到客户端一侧;
4. 客户端获取服务端的共享内存数据并映射到自己进程中(pionter() 函数);
5. 客户端通过调用IMemory的pointer函数, 完成映射并获取到共享内存的首地址;
6. pointer通过调用 geMemory, 向服务端的MemoryHeapBase请求共享内存的数据参数(内存代表的文件描述符、内存大小、访问权限和内存偏移量, 都是内存映射必不可少的参数)。
7. 通过mmap映射内存到客户端, 最后返回映射的共享内存首地址,这样, 两个进程之间就可以共享申请的匿名共享内存了。
注意:
由于虚拟内存的存在,文件描述符号是不能在两个进程之间传递的, 传递过去是无效的。这里可以传递,是因为Android的Binder在驱动侧做了适配, 在传递到另外一个进程的时候,binder驱动会自动查找并分配最小未使用的FD给另一个进程, 所以两个进程使用的FD可能是不一样的。
从pointer开始:
// 1. pointer
void* IMemory::pointer() const {
ssize_t offset;
//获取服务端的MemoryHeapBase的代理IMemoryHeap对象;
sp<IMemoryHeap> heap = getMemory(&offset);
//通过IMemoryHeap的base()函数调用getBase() 向服务端请求内存映射所需要的参数并映射到该进程, 最后返回映射的内存首地址;
void* const base = heap!=0 ? heap->base() : MAP_FAILED;
if (base == MAP_FAILED)
return 0;
//返回的是首地址+偏移量, 一般没有偏移的话,那就是0了(参考mmap的offset参数);
return static_cast<char*>(base) + offset;
}
//2. 调用getMemory获取IMemoryHeap对象
sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
{
if (mHeap == 0) {
Parcel data, reply;
data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
//获取MemoryHeapBase代理
if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
sp<IBinder> heap = reply.readStrongBinder(); //获取fd
ssize_t o = reply.readInt32(); //获取offset
size_t s = reply.readInt32(); //获取内存大小
if (heap != 0) {
mHeap = interface_cast<IMemoryHeap>(heap);
if (mHeap != 0) {
size_t heapSize = mHeap->getSize();
if (s <= heapSize
&& o >= 0
&& (static_cast<size_t>(o) <= heapSize - s)) {
mOffset = o;
mSize = s;
} else {
// Hm.
android_errorWriteWithInfoLog(0x534e4554,
"26877992", -1, NULL, 0);
mOffset = 0;
mSize = 0;
}
}
}
}
}
//存储获取的数据
if (offset) *offset = mOffset; //偏移量
if (size) *size = mSize; //内存大小
return (mSize > 0) ? mHeap /*MemoryHeap*/: 0;
}
//3. ############BnMemory即MemoryHeapBase, MemoryHeapBase是继承自BnMemory的服务端代表
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;
//调用MemoryHeapBase的getMemory(参考上面MemoryHeapBase::getMemory的说明)
//并将获取的IMemoryBase和offset, size写入到客户端
reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) );
reply->writeInt32(offset);
reply->writeInt32(size);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// 4. 获取映射首地址
void* BpMemoryHeap::getBase() const {
assertMapped(); //如果已经映射,返回首地址,还没有映射,则先映射再返回映射的首地址。
return mBase; //返回映射的首地址。
}
// 5. 断定使得否已经映射过了
void BpMemoryHeap::assertMapped() const
{
if (mHeapId == -1) {
sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
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);
}
}
}
// 6. 真正的开始映射
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(); //文件描述符
ssize_t size = reply.readInt32(); // 内存大小
uint32_t flags = reply.readInt32(); // 访问权限标志
uint32_t offset = reply.readInt32(); // 偏移量
ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
IInterface::asBinder(this).get(),
parcel_fd, size, err, strerror(-err));
Mutex::Autolock _l(mLock);
if (mHeapId == -1) {
int fd = dup( parcel_fd );
ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
int access = PROT_READ;
if (!(flags & READ_ONLY)) {
access |= PROT_WRITE;
}
mRealHeap = true;
//调用mmap开始映射
mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
if (mBase == MAP_FAILED) {
ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
IInterface::asBinder(this).get(), size, fd, strerror(errno));
close(fd);
} else {
//记录映射完成的数据。
mSize = size;
mFlags = flags;
mOffset = offset;
android_atomic_write(fd, &mHeapId);
}
}
}
}
定义公共的数据结构和枚举变量:
typedef struct DataSt {
int id;
int flag;
int tagIndex;
int dataSize;
int type;
char data[0]; //可扩展数据段
}DataSt_t;
enum {
CMD_START = IBinder::FIRST_CALL_TRANSACTION,
CMD_BINDER_OBJ = CMD_START + 1,
};
发送端:
//发送端------------------------------------------------------------------------------
sp<IMemory> Client::allocateMem(size_t size, const char *name) {
//申请用于扩展字段,头申请自己的空间
sp<MemoryHeapBase> heap = new MemoryHeapBase(sizeof(DataSt_t) + size, 0, name);
sp<IMemory> mem = new MemoryBase(heap, 0, size);
return mem;
}
void Client::fillData(sp<IMemory> mem) {
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
auto dataSt = static_cast<DataSt_t*>(heap->base() + offset);
dataSt->id = 0x11223344;
dataSt->flag = 0x44332211;
dataSt->tagIndex = mSessionId;
dataSt->type = 'TYPE';
dataSt->dataSize = sizeof(int);
//扩展数据段
int data = 5;
memcpy(dataSt->data, &data, sizeof(int));
}
int Client::deleteMem() {
mMem = nullptr;
return Result::OK;
}
void Client::sendBinder(sp<IMemory> mem) {
if (mServer != nullptr) {
Parcel data, reply;
携带Bindr对象,需要借助IInterface::asBinder接口.
data.writeStrongBinder(IInterface::asBinder(mem));
if (mServer->transact(CMD_BINDER_OBJ, data, &reply) != 0) {
//....
} else {
//....
}
}
}
int main() {
//模拟的执行方法,这里是客户端执行段
sp<IMemory> mem = allocateMem(1920 * 1080, "IMemory_test");
fillData(mem);
sendBinder(mem);
}
接收端
//接收端:-----------------------------------------------------------------------------
status_t Server::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case CMD_BINDER_OBJ: {
ALOGD("Memory callback.");
//接收Binder对象, 使用interface_cast(data.readStrongBinder());
//或者IMemory::asInterface(data.readStrongBinder())转换为对应的Binder接口
sp<IMemory> mem = interface_cast<IMemory>(data.readStrongBinder());
ssize_t offset = 0;
size_t size = 0;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
auto dataSt = (DataSt_t *)(heap->base() + offset);
if (dataSt == nullptr) {
ALOGE("dataSt == nullptr");
return -1;
}
ALOGD("offset= %d, size= %d", offset, size);
ALOGD("typedef struct DataSt {\n"
" int id = %x;\n"
" int flag = %x;\n"
" int tagIndex = %d;\n"
" int dataSize = %d;\n"
" int type = %d;\n"
" char data[0] = %d;\n"
"}DataSt_t;", dataSt->id, dataSt->flag, dataSt->tagIndex,
dataSt->dataSize, dataSt->type,
((uint32_t*)(dataSt->data))[0]);
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
使用后,客户端和服务端的addr 是指向同一块地址内存的相同偏移量的, 也就是共享内存的首地址。
IMemoryTest Demo:
https://gitee.com/bossagit11/android-imemory-test