在Android 匿名共享内存驱动源码分析中介绍了匿名共享内存的驱动实现过程,本文在Android匿名共享内存驱动基础上,介绍Android匿名共享内存对外Android系统的匿名共享内存子系统的主体是以驱动程序的形式实现在内核空间的,同时在应用程序框架层提供了Java调用接口。在Android应用程序框架层,提供了一个MemoryFile接口来封装了匿名共享内存文件的创建和使用,它实现在frameworks/base/core/java/android/os/MemoryFile.java
[cpp]
public MemoryFile(String name, int length) throws IOException {
mLength = length;
//打开"/dev/ashmem"设备文件
mFD = native_open(name, length);
if (length > 0) {
//将打开的"/dev/ashmem"设备文件映射到进程虚拟地址空间中
mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
} else {
mAddress = 0;
}
}
native_open函数是一个本地函数,通过JNI实现在C++层,代码位于frameworks\base\core\jni\android_os_MemoryFile.cpp
[cpp]
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
{
//字符串转换
const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
//打开设备文件"/dev/ashmem",并修改设备文件名称及共享内存大小
int result = ashmem_create_region(namestr, length);
if (name)
env->ReleaseStringUTFChars(name, namestr);
if (result < 0) {
jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
return NULL;
}
//设备文件句柄转换
return jniCreateFileDescriptor(env, result);
}
函数首先将Java层传过来的你们共享内存名称转换为C++层的字符串,然后调用ashmem_create_region函数创建一个名为dev/ashmem/的匿名共享内存,并且修改该共享内存的名称及大小,然后将创建的匿名共享内存设备文件句柄值返回到Java空间中。函数ashmem_create_region在Android 匿名共享内存C接口分析中有详细分析,该接口函数就是用于创建一块匿名共享内存。
在Java空间构造MemoryFile对象时,首先打开/dev/ashmem设备文件并在内核空间创建一个ashmem_area,接着需要将内核空间分配的共享内存地址映射到进程虚拟地址空间中来,映射过程是通过native_mmap函数来完成的。
[cpp]
static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
jint length, jint prot)
{
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (!result)
jniThrowException(env, "java/io/IOException", "mmap failed");
return result;
}
该函数直接调用mmap来实现地址空间映射,注意标志位MAP_SHARED,表示该缓冲区以共享方式映射。映射过程是由Ashmem驱动来完成,Android 匿名共享内存驱动源码分析详细分析了Android匿名共享内存的实现过程。在构造MemoryFile对象时完成了匿名共享内存的创建及地址空间的映射过程,将创建的匿名共享内存的大小保存到MemoryFile的成员变量mLength中,成员变量mFD保存创建的匿名共享内存的文件描述符,成员变量mAddress保存匿名共享内存映射到进程地址空间的起始地址。有了这些信息后,就可以直接使用该匿名共享内存了。
匿名共享内存读
对匿名共享内存的读取操作,在Java空间被封装成MemoryInputStream来完成,该类继承于输入流InputStream,并对外提供了read方法,定义如下:
[cpp]
@Override
public int read() throws IOException {
if (mSingleByte == null) {
mSingleByte = new byte[1];
}
int result = read(mSingleByte, 0, 1);
if (result != 1) {
return -1;
}
return mSingleByte[0];
}
@Override
public int read(byte buffer[], int offset, int count) throws IOException {
if (offset < 0 || count < 0 || offset + count > buffer.length) {
// readBytes() also does this check, but we need to do it before
// changing count.
throw new IndexOutOfBoundsException();
}
count = Math.min(count, available());
if (count < 1) {
return -1;
}
int result = readBytes(buffer, mOffset, offset, count);
if (result > 0) {
mOffset += result;
}
return result;
}
MemoryInputStream类提供了两个read重载方法,第一个无参read方法调用有参read方法来读取1字节的数据,而有参read方法的数据读取过程是调用MemoryInputStream的外部类MemoryFile的readBytes方法来实现匿名共享内存数据的读取过程。
[cpp]
public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
throws IOException {
if (isDeactivated()) {
throw new IOException("Can't read from deactivated memory file.");
}
if (destOffset < 0 || destOffset > buffer.length || count < 0
|| count > buffer.length - destOffset
|| srcOffset < 0 || srcOffset > mLength
|| count > mLength - srcOffset) {
throw new IndexOutOfBoundsException();
}
return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
}
该函数也仅仅作了一些判断,然后直接调用本地方法native_read在C++空间完成数据读取,在构造MemoryFile对象时,已经打开并映射了dev/ashmem设备文件,因此在这里直接将打开该设备文件得到的文件句柄值传到C++空间,以正确读取指定的匿名共享内存中的内容,mAddress为匿名共享内存映射到进程地址空间中的起始地址。
[cpp]
static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
jint count, jboolean unpinned)
{
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (unpinned &
补充:移动开发 , Android ,