声明

  • 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…
  • 文中参考了很多书籍及博客内容,可能涉及的比较多先不具体列出来了;
  • 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:cm-14.1 Android系统启动过程分析(1)-如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机
  • 若对JNI不熟悉,可参考此专栏:Androd系统的JNI与NDK

1 Ashmem的架构

  Android 系统实现的 Ashmem 匿名共享内存子系统,用来在应用程序之间共享数据。Ashmem 与传统的Linux系统实现的共享内存一样,都是基于内核提供的临时文件系统tmpfs实现的,但是 Ashmem 对内存块进行了更为精细化的管理。应用程序可以动态地将一块匿名共享内存划分为若干个小块,当这些小块内存不再需要使用时,它们就可以被内存管理系统回收。通过这种动态的、分而治之的内存管理方式,Android系统就能够有效地使用系统内存,适应内存较小的移动设备环境。

  匿名共享内存系统是以Ashmem驱动程序为基础的,系统中所有的匿名共享内存都由Ashmem驱动程序负责分配和管理。Android系统在 Native 层提供了 C/C++ 调用接口和 Framework 层提供了 Java 调用接口。

  • 在Framework 层中,提供了两个C++类 MemoryBase 和 MemoryHeapBase,以及一个 Java 类 MemoryFile 来使用匿名共享内存。
  • 在运行时库 cutils 中,主要提供了三个C函数 ashmem_create_region、ashmem_pin_region 和 ashmem_unpin_region 来访问 Ashmem 驱动程序。

  Ashmem驱动程序在启动时,会创建一个 /dev/ashmem 设备文件,这样,运行时库 cutils 中的匿名共享内存接口就可以通过文件操作函数 open 和 ioctl 等来访问 Ashmem 驱动程序。

  传统的 Linux 系统使用一个整数来标志一块共享内存,而 Android 系统则使用一个文件描述符来标志一块匿名共享内存。使用文件描述符来描述一块匿名共享内存有两个好处:

  1. 可以方便地将它映射到进程的地址空间,从而可以直接访问它的内容;
  2. 可以使用 Binder 进程间通信机制来传输这个文件描述符,从而实现在不同的应用程序之间共享一块匿名内存。

  Binder 进程间通信机制使用一个类型为 BINDER_TYPE_FD 的 Binder 对象来描述一个文件描述符,当 Binder 驱动程序发现进程间通信数据中包含有这种 Binder 对象时,就会将对应的文件描述符复制到目标进程中,从而实现在两个进程中共享同一个文件。

2 Ashmem子系统 Java 访问接口分析

  在Android的 Framework 层中通过使用接口 MemoryFile 来封装匿名共享内存文件的创建和使用接口 MemoryFile 在:frameworks/base/core/java/android/os/MemoryFile.java,具体实现代码如下所示:

public class MemoryFile
{private static String TAG = "MemoryFile";// mmap(2) protection flags from <sys/mman.h>private static final int PROT_READ = 0x1;private static final int PROT_WRITE = 0x2;private static native FileDescriptor native_open(String name, int length) throws IOException;// returns memory address for ashmem regionprivate static native long native_mmap(FileDescriptor fd, int length, int mode)throws IOException;private static native void native_munmap(long addr, int length) throws IOException;private static native void native_close(FileDescriptor fd);private static native int native_read(FileDescriptor fd, long address, byte[] buffer,int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;private static native void native_write(FileDescriptor fd, long address, byte[] buffer,int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;private static native int native_get_size(FileDescriptor fd) throws IOException;private FileDescriptor mFD;        // ashmem file descriptorprivate long mAddress;   // address of ashmem memoryprivate int mLength;    // total length of our ashmem regionprivate boolean mAllowPurging = false;  // true if our ashmem region is unpinned/*** 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, must be non-negative.* @throws IOException if the memory file could not be created.*/public MemoryFile(String name, int length) throws IOException {mLength = length;if (length >= 0) {mFD = native_open(name, length);} else {throw new IOException("Invalid length: " + length);}if (length > 0) {mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);} else {mAddress = 0;}}/*** Closes the memory file. If there are no other open references to the memory* file, it will be deleted.*/public void close() {deactivate();if (!isClosed()) {native_close(mFD);}}/*** Unmaps the memory file from the process's memory space, but does not close it.* After this method has been called, read and write operations through this object* will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.** @hide*/void deactivate() {if (!isDeactivated()) {try {native_munmap(mAddress, mLength);mAddress = 0;} catch (IOException ex) {Log.e(TAG, ex.toString());}}}/*** Checks whether the memory file has been deactivated.*/private boolean isDeactivated() {return mAddress == 0;}/*** Checks whether the memory file has been closed.*/private boolean isClosed() {return !mFD.valid();}@Overrideprotected void finalize() {if (!isClosed()) {Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");close();}}/*** Returns the length of the memory file.** @return file length.*/public int length() {return mLength;}/*** Is memory file purging enabled?** @return true if the file may be purged.*/public boolean isPurgingAllowed() {return mAllowPurging;}/*** Enables or disables purging of the memory file.** @param allowPurging true if the operating system can purge the contents* of the file in low memory situations* @return previous value of allowPurging*/synchronized public boolean allowPurging(boolean allowPurging) throws IOException {boolean oldValue = mAllowPurging;if (oldValue != allowPurging) {native_pin(mFD, !allowPurging);mAllowPurging = allowPurging;}return oldValue;}/*** Creates a new InputStream for reading from the memory file.*@return InputStream*/public InputStream getInputStream() {return new MemoryInputStream();}/*** Creates a new OutputStream for writing to the memory file.*@return OutputStream*/public OutputStream getOutputStream() {return new MemoryOutputStream();}/*** Reads bytes from the memory file.* Will throw an IOException if the file has been purged.** @param buffer byte array to read bytes into.* @param srcOffset offset into the memory file to read from.* @param destOffset offset into the byte array buffer to read into.* @param count number of bytes to read.* @return number of bytes read.* @throws IOException if the memory file has been purged or deactivated.*/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);}/*** Write bytes to the memory file.* Will throw an IOException if the file has been purged.** @param buffer byte array to write bytes from.* @param srcOffset offset into the byte array buffer to write from.* @param destOffset offset  into the memory file to write to.* @param count number of bytes to write.* @throws IOException if the memory file has been purged or deactivated.*/public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)throws IOException {if (isDeactivated()) {throw new IOException("Can't write to deactivated memory file.");}if (srcOffset < 0 || srcOffset > buffer.length || count < 0|| count > buffer.length - srcOffset|| destOffset < 0 || destOffset > mLength|| count > mLength - destOffset) {throw new IndexOutOfBoundsException();}native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);}/*** Gets a FileDescriptor for the memory file.** The returned file descriptor is not duplicated.** @throws IOException If the memory file has been closed.** @hide*/public FileDescriptor getFileDescriptor() throws IOException {return mFD;}/*** Returns the size of the memory file that the file descriptor refers to,* or -1 if the file descriptor does not refer to a memory file.** @throws IOException If <code>fd</code> is not a valid file descriptor.** @hide*/public static int getSize(FileDescriptor fd) throws IOException {return native_get_size(fd);}private class MemoryInputStream extends InputStream {private int mMark = 0;private int mOffset = 0;private byte[] mSingleByte;@Overridepublic int available() throws IOException {if (mOffset >= mLength) {return 0;}return mLength - mOffset;}@Overridepublic boolean markSupported() {return true;}@Overridepublic void mark(int readlimit) {mMark = mOffset;}@Overridepublic void reset() throws IOException {mOffset = mMark;}@Overridepublic 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];}@Overridepublic 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;}@Overridepublic long skip(long n) throws IOException {if (mOffset + n > mLength) {n = mLength - mOffset;}mOffset += n;return n;}}private class MemoryOutputStream extends OutputStream {private int mOffset = 0;private byte[] mSingleByte;@Overridepublic void write(byte buffer[], int offset, int count) throws IOException {writeBytes(buffer, offset, mOffset, count);mOffset += count;}@Overridepublic void write(int oneByte) throws IOException {if (mSingleByte == null) {mSingleByte = new byte[1];}mSingleByte[0] = (byte)oneByte;write(mSingleByte, 0, 1);}}
}

  构造方法 MemoryFile 以指定的字符串调用了JNI方法 native_open,目的是建立一个匿名共享内存文件,这样可以得到一个文件描述符。然后使用这个文件描述符为参数调用JNI方法natvie_mmap,并把匿名共享内存文件映射到进程空间中这样就可以通过映射得到地址空间的方式直接访问内存数据。
  成员函数 readBytes 用于读取某一块匿名共享内存的内容,成员函数 writeBytes 用于写入某一块匿名共享内存的内容,成员函数 isDeactivated 用于保证匿名共享内存已经被映射到进程的地址空间中。

  以上native方法对应实现在源码文件:frameworkslbaselcorejniandroid_os_MemoryFile.cpp 中,具体实现代码如下所示:

#define LOG_TAG "MemoryFile"
#include <utils/Log.h>#include <cutils/ashmem.h>
#include "core_jni_helpers.h"
#include "JNIHelp.h"
#include <unistd.h>
#include <sys/mman.h>namespace android {static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
{const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);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);
}static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,jint length, jint prot)
{int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);if (result == MAP_FAILED) {jniThrowException(env, "java/io/IOException", "mmap failed");}return reinterpret_cast<jlong>(result);
}static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jlong addr, jint length)
{int result = munmap(reinterpret_cast<void *>(addr), length);if (result < 0)jniThrowException(env, "java/io/IOException", "munmap failed");
}static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
{int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);if (fd >= 0) {jniSetFileDescriptorOfFD(env, fileDescriptor, -1);close(fd);}
}static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,jint count, jboolean unpinned)
{int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {ashmem_unpin_region(fd, 0, 0);jniThrowException(env, "java/io/IOException", "ashmem region was purged");return -1;}env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);if (unpinned) {ashmem_unpin_region(fd, 0, 0);}return count;
}static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,jint count, jboolean unpinned)
{int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {ashmem_unpin_region(fd, 0, 0);jniThrowException(env, "java/io/IOException", "ashmem region was purged");return -1;}env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);if (unpinned) {ashmem_unpin_region(fd, 0, 0);}return count;
}static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
{int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));if (result < 0) {jniThrowException(env, "java/io/IOException", NULL);}
}static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,jobject fileDescriptor) {int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);// Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.// ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel// should return ENOTTY for all other valid file descriptorsint result = ashmem_get_size_region(fd);if (result < 0) {if (errno == ENOTTY) {// ENOTTY means that the ioctl does not apply to this object,// i.e., it is not an ashmem region.return (jint) -1;}// Some other error, throw exceptionjniThrowIOException(env, errno);return (jint) -1;}return (jint) result;
}static const JNINativeMethod methods[] = {{"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},{"native_mmap",  "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap},{"native_munmap", "(JI)V", (void*)android_os_MemoryFile_munmap},{"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},{"native_read",  "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read},{"native_write", "(Ljava/io/FileDescriptor;J[BIIIZ)V", (void*)android_os_MemoryFile_write},{"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},{"native_get_size", "(Ljava/io/FileDescriptor;)I",(void*)android_os_MemoryFile_get_size}
};int register_android_os_MemoryFile(JNIEnv* env)
{return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods));
}}

Android系统的Ashmem匿名共享内存子系统分析(4)- Ashmem子系统的 Java访问接口相关推荐

  1. 安卓ashmem(匿名共享内存映射)学习native篇

    转自:http://blog.csdn.net/u010657219/article/details/41248965 先看看之前三个进程的关系: 由于这里用到了Binder进程间通信机制,这里再次贴 ...

  2. Android 匿名共享内存驱动源码分析

    原址 Android系统的匿名共享内存Ashmem驱动程序利用了Linux的共享内存子系统导出的接口来实现,本文通过源码分析方式详细介绍Android系统的匿名共享内存机制.在Android系统中,匿 ...

  3. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划

    在Android系统中,提供了独特的匿名共享内存子系统Ashmem(Anonymous Shared Memory),它以驱动程序的形式实现在内核空间中.它有两个特点,一是能够辅助内存管理系统来有效地 ...

  4. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6664554 在上一文章Android系统匿名共 ...

  5. Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析

    出自:http://blog.csdn.net/luoshengyang/article/details/6939890 在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享 ...

  6. Android 匿名共享内存C++接口分析

    原址 在上一篇Android 匿名共享内存C接口分析中介绍了Android系统的匿名共享内存C语言访问接口,本文在前文的基础上继续介绍Android系统的匿名共享内存提供的C++访问接口.在C++层通 ...

  7. Android 匿名共享内存C接口分析

    原址 在Android 匿名共享内存驱动源码分析中详细分析了匿名共享内存在Linux内核空间的实现,虽然内核空间实现了匿名共享内存,但仍然需要在用户空间为用户使用匿名共享内存提供访问接口.Androi ...

  8. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析

    在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们系统地介绍了Android系统匿名共享内存的实现原理,其中着重介绍了 ...

  9. Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(6)

    接下来,我们再来看看server模块的实现.在external/ashmem/common目录下,只有一个源文件SharedBufferServer.cpp,它实现了内存共享服务SharedBuffe ...

最新文章

  1. oracle11g ora00838,管理oracle11g內存設置 解決ora-02097 ora-00838 ora-00845報錯問題
  2. 手机用appnium,web自动化用eclips+webdriver2
  3. Java对象的四种引用方式
  4. day22-面向对象之封装
  5. webserver之定时器
  6. 低压锅炉行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  7. 在线运行此php解密navicat导出的密码!(用于navicat已经连接数据库但是忘记了密码)
  8. 【超图+CESIUM】【基础API使用示例】28、超图|CESIUM -【坐标转换】世界坐标转经纬度
  9. 基于java的化妆品购物商城微信小程序的设计与实现 毕业设计毕设参考
  10. 第二人生的源码分析(3)程序入口点
  11. 微型计算机登录密码忘记了怎么办,一种基于微型计算机的密码锁的制作方法
  12. Shallow Size 和 Retained Size
  13. matlab析取范式求主析取范式用电脑,(p∧q)∨r 求其主析取范式 再用主析取范式求主合取范式...
  14. 【京东电商网站主界面仿写——CSS第一部分】
  15. Arduino框架下对ESP32 NVS非易失性存储解读以及应用示例
  16. CISP证书的基本常识
  17. 巴菲特:我可以发2100万个巴菲特币|附视频
  18. Unity 中 print 和 Debug.Log 的区别
  19. 【springboot进阶】RestTemplate 集成 okhttp3 请求带p12证书
  20. 软件版本各阶段英文名称

热门文章

  1. Python中将科学计数法(或以e为底的自然对数)字符串转换为float浮点数
  2. 水下机器人项目!大创!包含solidworks模型,程序,PCB,上位机,实物视频演示,项目报告。
  3. Scalar-multiplication算法集
  4. re 模块 常规方法使用
  5. 层次聚类、k_means聚类-python源码
  6. GBASE 8C——SQL参考6 sql语法(12)
  7. “世界杰出女科学家成就奖”组委会宣布2021年度五位获奖者
  8. cvs配电保护断路器_施耐德CVS系列热磁式保护断路器三段保护断路器
  9. 【linux中Telnet服务的安装】
  10. Linux以太网卡软硬件架构