android跨进程实现共享内存
前言
业务场景:A进程需要拷贝大buffer到B进程,且需要快,越快越好!
业务方:A & B双方都是普通app;A是普通app,B是native服务程序(native c++实现,在android源码下编译的服务程序);A & B都是native服务程序
首先,创建一块共享内存ashmem
,则不管是普通app,还是系统服务程序,都有权限创建。但是,创建好了,需要另一边做mmap,而做mmap,需要传递共享内存的文件句柄fd,这个fd虽然是一个int值,但是通过通过socket啥的传递过去,是没办法做mmap的,因为另一个进程B,根本不认识这个int。怎么办?靠binder
! binder可以把文件句柄,跨进程传输,保证打开的是同一个文件!
所以,android要实现共享内存,需要基于binder + ashmem
。
场景1: A&B都是普通app
这种比较简单,可以参考https://github.com/dev-area/ashmem。
场景2: A&B都是native服务程序
这种比较简单,可以参考https://cloud.tencent.com/developer/article/1490314。
场景3: A是普通app,B是系统服务程序
方案:A请求B创建一块共享内存,B创建,通过binder返回文件句柄,A根据文件句柄做mmap,得到写入数据的指针。
A写入数据,通过binder通知B读取,B读取,返回读取结果。
A端请求的代码,得到文件句柄shmFd:
public void createShm() {
if (mService != null) {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInt(ShareMemSize);
data.writeString(ShareMemName);
try {
Log.i(TAG, "start request to create shm");
mService.transact(CREATE_SHM, data, reply, 0);
Log.i(TAG, "transact done");
ParcelFileDescriptor pfd = reply.readFileDescriptor();
shmFd = pfd.getFd();
Log.e(TAG, "get shm file descriptor, shmFd: " + shmFd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
A端根据shmFd做map,得到共享内存的写入地址:
shmWriteAddr = (char *) mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, shmFd, 0);
if (shmWriteAddr == (void *) -1) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "do mmap fail %s", strerror(errno));
return -1;
}
B端响应的代码:
status_t BnDemoAPI::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
//Here, to check the calling permissions
IPCThreadState* self = IPCThreadState::self();
ALOGE("Calling MSG: PID=%d, UID=%d",self->getCallingPid(),self->getCallingUid());
//For Client Read exception code
//reply->writeInt32(0);
std::cout << "calling, code " << code << std::endl;
switch(code)
{
case GET_SHM_FD: {
//CHECK_INTERFACE(IDemoAPI, data, reply);
int shmSize = data.readInt32();
String8 s8(data.readString16());
ALOGE("get shm name %s ", s8.string());
string shmName(s8.string());
if(shmName.empty()) {
shmName = "alphagame";
}
mShmInstance.reset(nullptr);
mShmInstance.reset(new ShmInstance(shmName, shmSize));
int fd = mShmInstance->getShmFd();
std::cout << "getShmFd: fd " << fd << " size " << shmSize << " shmName " << shmName <<std::endl;
reply->writeFileDescriptor(fd);
return NO_ERROR;
}
break;
case BUFFER_UPDATE:
{
if(mShmInstance != nullptr) {
mShmInstance->readShmData();
}
return NO_ERROR;
}break;
default:break;
}
return NO_ERROR;
}
B端创建SHM的关键代码:
int ShmInstance::getShmFd()
{
int fd = open(DEVASHMEM, O_RDWR);
if(fd < 0)
{
return -1;
}
int ret = ioctl(fd, ASHMEM_SET_NAME, SHNAME);
if(ret < 0){
close(fd);
return -1;
}
ret = ioctl(fd, ASHMEM_SET_SIZE, shmSize);
if(ret < 0){
close(fd);
return -1;
}
shmAddr = (char*)mmap(NULL, shmSize , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(NULL == shmAddr)
{
return -1;
}
shmFd = fd;
std::cout << "create shm success" << std::endl;
return fd;
}
完整项目地址:
https://github.com/newchenxf/AshMemBaseBinder
[1]: Android Binder传递文件描述符原理分析
[2]: https://github.com/dev-area/ashmem
[3]: https://github.com/qianjigui/android_system_service_example