在Android系统中,提供了独特的匿名共享内存子系统Ashmem(Anonymous Shared Memory),它以驱动程序的形式实现在内核空间中。它有两个特点,一是能够辅助内存管理系统来有效地管理不再使用的内存块,二是它通过Binder进程间通信机制来实现进程间的内存共享。本文中,我们将通过实例来简要介绍Android系统的匿名共享内存的使用方法,使得我们对Android系统的匿名共享内存机制有一个感性的认识,为进一步学习它的源代码实现打下基础。
Android系统的匿名共享内存子系统的主体是以驱动程序的形式实现在内核空间的,同时,在系统运行时库层和应用程序框架层提供了访问接口,其中,在系统运行时库层提供了C/C++调用接口,而在应用程序框架层提供了Java调用接口。这里,我们将直接通过应用程序框架层提供的Java调用接口来说明匿名共享内存子系统Ashmem的使用方法,毕竟我们在Android开发应用程序时,是基于Java语言的,而实际上,应用程序框架层的Java调用接口是通过JNI方法来调用系统运行时库层的C/C++调用接口,最后进入到内核空间的Ashmem驱动程序去的。
我们在这里举的例子是一个名为Ashmem的应用程序,它包含了一个Server端和一个Client端实现,其中,Server端是以Service的形式实现的,在这里Service里面,创建一个匿名共享内存文件,而Client是一个Activity,这个Activity通过Binder进程间通信机制获得前面这个Service创建的匿名共享内存文件的句柄,从而实现共享。在Android应用程序框架层,提供了一个MemoryFile接口来封装了匿名共享内存文件的创建和使用,它实现在frameworks/base/core/java/android/os/MemoryFile.java文件中。下面,我们就来看看Server端是如何通过MemoryFile类来创建匿名共享内存文件的以及Client是如何获得这个匿名共享内存文件的句柄的。
在MemoryFile类中,提供了两种创建匿名共享内存的方法,我们通过MemoryFile类的构造函数来看看这两种使用方法:
public class MemoryFile { ...... /** * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). * @param length of the memory file in bytes. * @throws IOException if the memory file could not be created. */ public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); mOwnsRegion = true; } /** * Creates a reference to an existing memory file. Changes to the original file * will be available through this reference. * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. * * @param fd File descriptor for an existing memory file, as returned by * {@link #getFileDescriptor()}. This file descriptor will be closed * by {@link #close()}. * @param length Length of the memory file in bytes. * @param mode File mode. Currently only "r" for read-only access is supported. * @throws NullPointerException if <code>fd</code> is null. * @throws IOException If <code>fd</code> does not refer to an existing memory file, * or if the file mode of the existing memory file is more restrictive * than <code>mode</code>. * * @hide */ public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException { if (fd == null) { throw new NullPointerException("File descriptor is null."); } if (!isMemoryFile(fd)) { throw new IllegalArgumentException("Not a memory file."); } mLength = length; mFD = fd; mAddress = native_mmap(mFD, length, modeToProt(mode)); mOwnsRegion = false; } ...... }从注释中,我们可以看出这两个构造函数的使用方法,这里就不再详述了。两个构造函数的主要区别是第一个参数,第一种构造方法是以指定的字符串调用JNI方法native_open来创建一个匿名共享内存文件,从而得到一个文件描述符,接着就以这个文件描述符为参数调用JNI方法natvie_mmap把这个匿名共享内存文件映射在进程空间中,然后就可以通过这个映射后得到的地址空间来直接访问内存数据了;第二种构造方法是以指定的文件描述符来直接调用JNI方法natvie_mmap把这个匿名共享内存文件映射在进程空间中,然后进行访问,而这个文件描述符就必须要是一个匿名共享内存文件的文件描述符,这是通过一个内部函数isMemoryFile来验证的,而这个内部函数isMemoryFile也是通过JNI方法调用来进一步验证的。前面所提到的这些JNI方法调用,最终都是通过系统运行时库层进入到内核空间的Ashmem驱动程序中去,不过这里我们不关心这些JNI方法、系统运行库层调用以及Ashmem驱动程序的具体实现,在接下来的两篇文章中,我们将会着重介绍,这里我们只关注MemoryFile这个类的使用方法。