2.43 Android匿名共享内存
2 minute read
Android的匿名共享内存
- Android可以使用Linux的一切IPC通信方式,包括共享内存,不过Android主要使用的方式是匿名共享内存Ashmem(Anonymous Shared Memory), MemoryFile是Android为匿名共享内存而封装的一个对象,这里通过使用MemoryFile来分析,Android中如何利用共享内存来实现大数据传递,同时MemoryFile也是进程间大数据传递的一个手段
// IMemoryAidlInterface.aidl
interface IMemoryAidlInterface {
ParcelFileDescriptor getParcelFileDescriptor();
}
// MemoryFetchService
public class MemoryFetchService extends Service {
public IBinder onBind(Intent intent) {
return new MemoryFetchStub();
}
static class MemoryFetchStub extends IMemoryAidlInterface.Stub {
public ParcelFileDescriptor getParcelFileDescriptor() throws RemoteException {
MemoryFile memoryFile = null;
...
memoryFile = new MemoryFile("test_memory", 1024);
memoryFile.getOutputStream().write(new byte[]{1, 2, 3, 4, 5});
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);
return ParcelFileDescriptor.dup(des);
...
}}}
// TestActivity.java
Intent intent = new Intent(MainActivity.this, MemoryFetchService.class);
bindService(intent, new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
byte[] content = new byte[10];
IMemoryAidlInterface iMemoryAidlInterface = IMemoryAidlInterface.Stub.asInterface(service);
ParcelFileDescriptor parcelFileDescriptor = iMemoryAidlInterface.getParcelFileDescriptor();
FileDescriptor descriptor = parcelFileDescriptor.getFileDescriptor();
FileInputStream fileInputStream = new FileInputStream(descriptor);
fileInputStream.read(content);
...
}, Service.BIND_AUTO_CREATE);
MemoryFile
共享内存的分配与传递
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
{
const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
// ashmem_create_region创建共享内存
int result = ashmem_create_region(namestr, length);
...
return jniCreateFileDescriptor(env, result);
}
int ashmem_create_region(const char *name, size_t size)
{
int fd, ret;
// 调用这个open函数最终会进入到Ashmem驱动程序中的ashmem_open函数中去
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
...
// ASHMEM_DEVICE其实就是抽象的共享内存设备,它是一个杂项设备(字符设备的一种),在驱动加载之后,就会在/dev下ashem文件,之后用户就能够访问该设备文件,同一般的设备文件不同,它仅仅是通过内存抽象的,同普通的磁盘设备文件、串行端口字段设备文件不一样
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
...
}
...
}
struct file_operations ashmem_fops = {
.owner = THIS_MODULE,
.open = ashmem_open,
.release = ashmem_release,
.mmap = ashmem_mmap,
.unlocked_ioctl = ashmem_ioctl,
.compat_ioctl = ashmem_ioctl,
};
static int ashmem_open(struct inode *inode, struct file *file)
{
struct ashmem_area *asma;
int ret;
ret = nonseekable_open(inode, file);
...
asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);
...
return 0;
}
long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ashmem_area *asma = file->private_data;
long ret = -ENOTTY;
switch (cmd) {
......
case ASHMEM_SET_SIZE:
ret = -EINVAL;
if (!asma->file) {
ret = 0;
asma->size = (size_t) arg;
}
break;
......
}
return ret;
}
// 文件描述符fd是在前面open匿名设备文件/dev/ashmem获得的,有个这个文件描述符后,就可以直接通过mmap来执行内存映射操作了
static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
jint length, jint prot)
{
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
// 系统调用mmap,将共享内存映射到当前进程空间
jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
...
return result;
}
int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{
struct ashmem_area *asma = file->private_data;
int ret = 0;
...
if (!asma->file) {
char *name = ASHMEM_NAME_DEF;
struct file *vmfile;
/* ... and allocate the backing shmem file */
// 调用了Linux内核提供的shmem_file_setup函数来在临时文件系统tmpfs中创建一个临时文件,这个临时文件与Ashmem驱动程序创建的匿名共享内存对应
vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
...
// 表示当前进程空间中一块连续的虚拟地址空间
// 临时文件vmfile也会保存asma->file域中,这样,Ashmem驱动程序后面就可以通过在asma->file来操作这个匿名内存共享文件了。
asma->file = vmfile;
}
get_file(asma->file);
if (vma->vm_flags & VM_SHARED)
shmem_set_file(vma, asma->file);
else {
if (vma->vm_file)
fput(vma->vm_file);
//这个临时文件vmfile也会保存asma->file域中,这样,Ashmem驱动程序后面就可以通过在asma->file来操作这个匿名内存共享文件了。
vma->vm_file = asma->file;
}
...
}
匿名共享内存的优缺点
- 优点: 匿名共享内存不会占用Dalvik Heap与Native Heap,不会导致OOM
- 缺点: 过度使用会导致系统资源不足
- 共享存占用空间的计算,只会计算到第一个创建它的进程中,其他进程不将ashmem计算在内
参考