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

简介:

在上一文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划中,我们简要介绍了Android系统的匿名共享内存机制,其中,简要提到了它具有辅助内存管理系统来有效地管理内存的特点,但是没有进一步去了解它是如何实现的。在本文中,我们将通过分析Android系统的匿名共享内存Ashmem驱动程序的源代码,来深入了解它是如何辅助内存管理系

        Android系统的匿名共享内存Ashmem机制并没有自立山头,从头搞一套自己的共享内存机制,而是建立在Linux内核实现的共享内存的基础上的。与此同时,它又向Linux内存管理系统的内存回收算法注册接口,告诉Linux内存管理系统它的某些内存块不再使用了,可以被回收了,不过,这些不再使用的内存需要由它的使用者来告诉Ashmem驱动程序。通过这种用户-Ashmem驱动程序-内存管理系统三者的紧密合作,实现有效的内存管理机制,适合移动设备小内存的特点。

        Android系统的匿名共享内存Ashmem驱动程序利用了Linux的共享内存子系统导出的接口来实现自己的功能,因此,它的实现非常小巧,总共代码不到700行。虽然代码很少,但是这里不打算机械式地一行一行地阅读和分析Ashmem驱动程序的源代码,而是通过使用情景来分析,这样可以帮助我们清晰地理解它的实现原理。我们这里所说的使用情景,将从Android系统的应用程序框架层提供的匿名共享内存接口开始,经过系统运行时库层,最终到达驱动程序层,通过这样一个完整的过程来理解Android系统的匿名共享内存Ashmem机制。这里,我们将从上一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划介绍的Android应用程序框架层提供MemoryFile接口开始,分别介绍Android系统匿名共享内存的创建(open)、映射(mmap)、读写(read/write)以及锁定和解锁(pin/unpin)四个使用情景。

        在进入到这个四个使用情景前,我们先来看一下Ashmem驱动程序模块的初始化函数,看看它给用户空间暴露了什么接口,即它创建了什么样的设备文件,以及提供了什么函数来操作这个设备文件。Ashmem驱动程序实现在kernel/common/mm/ashmem.c文件中,它的模块初始化函数定义为ashmem_init:

 
 
  1. static struct file_operations ashmem_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .open = ashmem_open,  
  4.     .release = ashmem_release,  
  5.     .mmap = ashmem_mmap,  
  6.     .unlocked_ioctl = ashmem_ioctl,  
  7.     .compat_ioctl = ashmem_ioctl,  
  8. };  
  9.  
  10. static struct miscdevice ashmem_misc = {  
  11.     .minor = MISC_DYNAMIC_MINOR,  
  12.     .name = "ashmem",  
  13.     .fops = &ashmem_fops,  
  14. };  
  15.  
  16. static int __init ashmem_init(void)  
  17. {  
  18.     int ret;  
  19.  
  20.     ......  
  21.  
  22.     ret = misc_register(&ashmem_misc);  
  23.     if (unlikely(ret)) {  
  24.         printk(KERN_ERR "ashmem: failed to register misc device!\n");  
  25.         return ret;  
  26.     }  
  27.  
  28.     ......  
  29.  
  30.     return 0;  

 

       这里,我们可以看到,Ahshmem驱动程序在加载时,会创建一个/dev/ashmem的设备文件,这是一个misc类型的设备。注册misc设备是通过misc_register函数进行的,关于这个函数的详细实现,可以参考前面Android日志系统驱动程序Logger源代码分析一文,调用这个函数成功后,就会在/dev目录下生成一个ashmem设备文件了。同时,我们还可以看到,这个设备文件提供了open、mmap、release和ioctl四种操作。为什么没有read和write操作呢?这是因为读写共享内存的方法是通过内存映射地址来进行的,即通过mmap系统调用把这个设备文件映射到进程地址空间中,然后就直接对内存进行读写了,不需要通过read 和write文件操作,后面我们将会具体分析是如何实现的。
       有了这个基础之后,下面我们就分四个部分来分别介绍匿名共享内存的创建(open)、映射(mmap)、读写(read/write)以及锁定和解锁(pin/unpin)使用情景。

 

        一. 匿名共享内存的创建操作

        在Android应用程序框架层提供MemoryFile类的构造函数中,进行了匿名共享内存的创建操作,我们先来看一下这个构造函数的实现,它位于frameworks/base/core/java/android/os/MemoryFile.java文件中:

 
 
  1. public class MemoryFile  
  2. {  
  3.     ......  
  4.  
  5.     private static native FileDescriptor native_open(String name, int length) throws IOException;  
  6.       
  7.     ......  
  8.  
  9.     private FileDescriptor mFD;        // ashmem file descriptor  
  10.     ......  
  11.     private int mLength;    // total length of our ashmem region  
  12.       
  13.     ......  
  14.  
  15.     /**  
  16.     * Allocates a new ashmem region. The region is initially not purgable.  
  17.     *  
  18.     * @param name optional name for the file (can be null).  
  19.     * @param length of the memory file in bytes.  
  20.     * @throws IOException if the memory file could not be created.  
  21.     */ 
  22.     public MemoryFile(String name, int length) throws IOException {  
  23.         mLength = length;  
  24.         mFD = native_open(name, length);  
  25.         ......  
  26.     }  
  27.  
  28.     ......  

 

        这里我们看到,这个构造函数最终是通过JNI方法native_open来创建匿名内存共享文件。这个JNI方法native_open实现在frameworks/base/core/jni/adroid_os_MemoryFile.cpp文件中:

 
 
  1. static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)  
  2. {  
  3.     const char* namestr = (name ? env->GetStringUTFChars(nameNULL) : NULL);  
  4.  
  5.     int result = ashmem_create_region(namestr, length);  
  6.  
  7.     if (name)  
  8.         env->ReleaseStringUTFChars(name, namestr);  
  9.  
  10.     if (result < 0) {  
  11.         jniThrowException(env, "java/io/IOException""ashmem_create_region failed");  
  12.         return NULL;  
  13.     }  
  14.  
  15.     return jniCreateFileDescriptor(env, result);  

        这个函数又通过运行时库提供的接口ashmem_create_region来创建匿名共享内存,这个函数实现在system/core/libcutils/ashmem-dev.c文件中:

 
 
  1. /*  
  2.  * ashmem_create_region - creates a new ashmem region and returns the file  
  3.  * descriptor, or <0 on error  
  4.  *  
  5.  * `nameis an optional label to give the region (visible in /proc/pid/maps)  
  6.  * `sizeis the size of the region, in page-aligned bytes  
  7.  */  
  8. int ashmem_create_region(const char *name, size_t size)  
  9. {  
  10.     int fd, ret;  
  11.  
  12.     fd = open(ASHMEM_DEVICE, O_RDWR);  
  13.     if (fd < 0)  
  14.         return fd;  
  15.  
  16.     if (name) {  
  17.         char buf[ASHMEM_NAME_LEN];  
  18.  
  19.         strlcpy(buf, name, sizeof(buf));  
  20.         ret = ioctl(fd, ASHMEM_SET_NAME, buf);  
  21.         if (ret < 0)  
  22.             goto error;  
  23.     }  
  24.  
  25.     ret = ioctl(fd, ASHMEM_SET_SIZE, size);  
  26.     if (ret < 0)  
  27.         goto error;  
  28.  
  29.     return fd;  
  30.  
  31. error:  
  32.     close(fd);  
  33.     return ret;  

        这里,一共通过执行三个文件操作系统调用来和Ashmem驱动程序进行交互,分虽是一个open和两个ioctl操作,前者是打开设备文件ASHMEM_DEVICE,后者分别是设置匿名共享内存的名称和大小。

        在介绍这三个文件操作之前,我们先来了解一下Ashmem驱动程序的一个相关数据结构struct ashmem_area,这个数据结构就是用来表示一块共享内存的,它定义在kernel/common/mm/ashmem.c文件中:

 
 
  1. /*  
  2.  * ashmem_area - anonymous shared memory area  
  3.  * Lifecycle: From our parent file's open() until its release()  
  4.  * Locking: Protected by `ashmem_mutex'  
  5.  * Big Note: Mappings do NOT pin this structure; it dies on close()  
  6.  */  
  7. struct ashmem_area {  
  8.     char name[ASHMEM_FULL_NAME_LEN];/* optional name for /proc/pid/maps */  
  9.     struct list_head unpinned_list; /* list of all ashmem areas */  
  10.     struct file *file;      /* the shmem-based backing file */  
  11.     size_t size;            /* size of the mapping, in bytes */  
  12.     unsigned long prot_mask;    /* allowed prot bits, as vm_flags */  
  13. }; 

        域name表示这块共享内存的名字,这个名字会显示/proc/<pid>/maps文件中,<pid>表示打开这个共享内存文件的进程ID;域unpinned_list是一个列表头,它把这块共享内存中所有被解锁的内存块连接在一起,下面我们讲内存块的锁定和解锁操作时会看到它的用法;域file表示这个共享内存在临时文件系统tmpfs中对应的文件,在内核决定要把这块共享内存对应的物理页面回收时,就会把它的内容交换到这个临时文件中去;域size表示这块共享内存的大小;域prot_mask表示这块共享内存的访问保护位。

 

        在Ashmem驱动程中,所有的ashmem_area实例都是从自定义的一个slab缓冲区创建的。这个slab缓冲区是在驱动程序模块初始化函数创建的,我们来看一个这个初始化函数的相关实现:

 
 
  1. static int __init ashmem_init(void)  
  2. {  
  3.     int ret;  
  4.  
  5.     ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",  
  6.         sizeof(struct ashmem_area),  
  7.         0, 0, NULL);  
  8.     if (unlikely(!ashmem_area_cachep)) {  
  9.         printk(KERN_ERR "ashmem: failed to create slab cache\n");  
  10.         return -ENOMEM;  
  11.     }  
  12.  
  13.     ......  
  14.  
  15.     return 0;  

        全局变量定义在文件开头的地方:

 
 
  1. static struct kmem_cache *ashmem_area_cachep __read_mostly; 

       它的类型是struct kmem_cache,表示这是一个slab缓冲区,由内核中的内存管理系统进行管理。

 

        这里就是通过kmem_cache_create函数来创建一个名为"ashmem_area_cache"、对象大小为sizeof(struct ashmem_area)的缓冲区了。缓冲区创建了以后,就可以每次从它分配一个struct ashmem_area对象了。关于Linux内核的slab缓冲区的相关知识,可以参考前面Android学习启动篇一文中提到的一本参考书籍《Understanding the Linux Kernel》的第八章Memory Managerment。
 

        有了这些基础知识后,我们回到前面的ashmem_create_region函数中。

        首先是执行打开文件的操作:

 
 
  1. fd = open(ASHMEM_DEVICE, O_RDWR); 

        ASHMEM_DEVICE是一个宏,定义为:

 
 
  1. #define ASHMEM_DEVICE   "/dev/ashmem" 

         这里就是匿名共享内存设备文件/dev/ashmem了。

 

        从上面的描述我们可以知道,调用这个open函数最终会进入到Ashmem驱动程序中的ashmem_open函数中去:

 
 
  1. static int ashmem_open(struct inode *inode, struct file *file)  
  2. {  
  3.     struct ashmem_area *asma;  
  4.     int ret;  
  5.  
  6.     ret = nonseekable_open(inode, file);  
  7.     if (unlikely(ret))  
  8.         return ret;  
  9.  
  10.     asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);  
  11.     if (unlikely(!asma))  
  12.         return -ENOMEM;  
  13.  
  14.     INIT_LIST_HEAD(&asma->unpinned_list);  
  15.     memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);  
  16.     asma->prot_mask = PROT_MASK;  
  17.     file->private_data = asma;  
  18.  
  19.     return 0;  

        首先是通过nonseekable_open函数来设备这个文件不可以执行定位操作,即不可以执行seek文件操作。接着就是通过kmem_cache_zalloc函数从刚才我们创建的slab缓冲区ashmem_area_cachep来创建一个ashmem_area结构体了,并且保存在本地变量asma中。再接下去就是初始化变量asma的其它域,其中,域name初始为ASHMEM_NAME_PREFIX,这是一个宏,定义为:

 
 
  1. #define ASHMEM_NAME_PREFIX "dev/ashmem/" 
  2. #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1) 

        函数的最后是把这个ashmem_area结构保存在打开文件结构体的private_data域中,这样,Ashmem驱动程序就可以在其它地方通过这个private_data域来取回这个ashmem_area结构了。

 

        到这里,设备文件/dev/ashmem的打开操作就完成了,它实际上就是在Ashmem驱动程序中创建了一个ashmem_area结构,表示一块新的共享内存。

        再回到ashmem_create_region函数中,又调用了两次ioctl文件操作分别来设备这块新建的匿名共享内存的名字和大小。在kernel/comon/mm/include/ashmem.h文件中,ASHMEM_SET_NAME和ASHMEM_SET_SIZE的定义为:

 
 
  1. #define ASHMEM_NAME_LEN     256  
  2.  
  3. #define __ASHMEMIOC     0x77  
  4.  
  5. #define ASHMEM_SET_NAME     _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])  
  6. #define ASHMEM_SET_SIZE     _IOW(__ASHMEMIOC, 3, size_t) 

      先来看ASHMEM_SET_NAME命令的ioctl调用,它最终进入到Ashmem驱动程序的ashmem_ioctl函数中:

 
 
  1. static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     struct ashmem_area *asma = file->private_data;  
  4.     long ret = -ENOTTY;  
  5.  
  6.     switch (cmd) {  
  7.     case ASHMEM_SET_NAME:  
  8.         ret = set_name(asma, (void __user *) arg);  
  9.         break;  
  10.     ......  
  11.     }  
  12.  
  13.     return ret;  

       这里通过set_name函数来进行实际操作:

 
 
  1. static int set_name(struct ashmem_area *asma, void __user *name)  
  2. {  
  3.     int ret = 0;  
  4.  
  5.     mutex_lock(&ashmem_mutex);  
  6.  
  7.     /* cannot change an existing mapping's name */  
  8.     if (unlikely(asma->file)) {  
  9.         ret = -EINVAL;  
  10.         goto out;  
  11.     }  
  12.  
  13.     if (unlikely(copy_from_user(asma->name + ASHMEM_NAME_PREFIX_LEN,  
  14.                     name, ASHMEM_NAME_LEN)))  
  15.         ret = -EFAULT;  
  16.     asma->name[ASHMEM_FULL_NAME_LEN-1] = '\0';  
  17.  
  18. out:  
  19.     mutex_unlock(&ashmem_mutex);  
  20.  
  21.     return ret;  

        这个函数实现很简单,把用户空间传进来的匿名共享内存的名字设备到asma->name域中去。注意,匿名共享内存块的名字的内容分两部分,前一部分是前缀,这是在open操作时,由驱动程序默认设置的,固定为ASHMEM_NAME_PREFIX,即"dev/ashmem/";后一部分由用户指定,这一部分是可选的,即用户可以不调用ASHMEM_SET_NAME命令来设置匿名共享内存块的名字。

 

        再来看ASHMEM_SET_SIZE命令的ioctl调用,它最终也是进入到Ashmem驱动程序的ashmem_ioctl函数中:

 
 
  1. static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     struct ashmem_area *asma = file->private_data;  
  4.     long ret = -ENOTTY;  
  5.  
  6.     switch (cmd) {  
  7.     ......  
  8.     case ASHMEM_SET_SIZE:  
  9.         ret = -EINVAL;  
  10.         if (!asma->file) {  
  11.             ret = 0;  
  12.             asma->size = (size_t) arg;  
  13.         }  
  14.         break;  
  15.     ......  
  16.     }  
  17.  
  18.     return ret;  

        这个实现很简单,只是把用户空间传进来的匿名共享内存的大小值保存在对应的asma->size域中。

 

        这样,ashmem_create_region函数就执先完成了,层层返回,最后回到应用程序框架层提供的接口Memory的构造函数中,整个匿名共享内存的创建过程就完成了。前面我们说过过,Ashmem驱动程序不提供read和write文件操作,进程若要访问这个共享内存,必须要把这个设备文件映射到自己的进程空间中,然后进行直接内存访问,这就是我们下面要介绍的匿名共享内存设备文件的内存映射操作了。

        二. 匿名共享内存设备文件的内存映射操作

        在MemoryFile类的构造函数中,进行了匿名共享内存的创建操作后,下一步就是要把匿名共享内存设备文件映射到进程空间来了:

 
 
  1. public class MemoryFile  
  2. {  
  3.     ......  
  4.  
  5.     // returns memory address for ashmem region  
  6.     private static native int native_mmap(FileDescriptor fd, int length, int mode)  
  7.         throws IOException;  
  8.       
  9.     ......  
  10.  
  11.     private int mAddress;   // address of ashmem memory  
  12.       
  13.     ......  
  14.  
  15.     /**  
  16.     * Allocates a new ashmem region. The region is initially not purgable.  
  17.     *  
  18.     * @param name optional name for the file (can be null).  
  19.     * @param length of the memory file in bytes.  
  20.     * @throws IOException if the memory file could not be created.  
  21.     */ 
  22.     public MemoryFile(String name, int length) throws IOException {  
  23.         ......  
  24.         mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);  
  25.         ......  
  26.     }  

         映射匿名共享内存设备文件到进程空间是通过JNI方法native_mmap来进行的。这个JNI方法实现在frameworks/base/core/jni/adroid_os_MemoryFile.cpp文件中:

 
 
  1. static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,  
  2.         jint length, jint prot)  
  3. {  
  4.     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);  
  5.     jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);  
  6.     if (!result)  
  7.         jniThrowException(env, "java/io/IOException""mmap failed");  
  8.     return result;  

        这里的文件描述符fd是在前面open匿名设备文件/dev/ashmem获得的,有个这个文件描述符后,就可以直接通过mmap来执行内存映射操作了。这个mmap系统调用最终进入到Ashmem驱动程序的ashmem_mmap函数中:

 
 
  1. static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)  
  2. {  
  3.     struct ashmem_area *asma = file->private_data;  
  4.     int ret = 0;  
  5.  
  6.     mutex_lock(&ashmem_mutex);  
  7.  
  8.     /* user needs to SET_SIZE before mapping */  
  9.     if (unlikely(!asma->size)) {  
  10.         ret = -EINVAL;  
  11.         goto out;  
  12.     }  
  13.  
  14.     /* requested protection bits must match our allowed protection mask */  
  15.     if (unlikely((vma->vm_flags & ~asma->prot_mask) & PROT_MASK)) {  
  16.         ret = -EPERM;  
  17.         goto out;  
  18.     }  
  19.  
  20.     if (!asma->file) {  
  21.         char *name = ASHMEM_NAME_DEF;  
  22.         struct file *vmfile;  
  23.  
  24.         if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')  
  25.             name = asma->name;  
  26.  
  27.         /* ... and allocate the backing shmem file */  
  28.         vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);  
  29.         if (unlikely(IS_ERR(vmfile))) {  
  30.             ret = PTR_ERR(vmfile);  
  31.             goto out;  
  32.         }  
  33.         asma->file = vmfile;  
  34.     }  
  35.     get_file(asma->file);  
  36.  
  37.     if (vma->vm_flags & VM_SHARED)  
  38.         shmem_set_file(vma, asma->file);  
  39.     else {  
  40.         if (vma->vm_file)  
  41.             fput(vma->vm_file);  
  42.         vma->vm_file = asma->file;  
  43.     }  
  44.     vma->vm_flags |= VM_CAN_NONLINEAR;  
  45.  
  46. out:  
  47.     mutex_unlock(&ashmem_mutex);  
  48.     return ret;  

        这个函数的实现也很简单,它调用了Linux内核提供的shmem_file_setup函数来在临时文件系统tmpfs中创建一个临时文件,这个临时文件与Ashmem驱动程序创建的匿名共享内存对应。函数shmem_file_setup是Linux内核中用来创建共享内存文件的方法,而Linux内核中的共享内存机制其实是一种进程间通信(IPC)机制,它的实现相对也是比较复杂,Android系统的匿名共享内存机制正是由于直接使用了Linux内核共享内存机制,它才会很小巧,它站在巨人的肩膀上了。关于Linux内核中的共享内存的相关知识,可以参考前面Android学习启动篇一文中提到的一本参考书籍《Linux内核源代码情景分析》的第六章传统的Unix进程间通信第七小节共享内存。
 

 

        通过shmem_file_setup函数创建的临时文件vmfile最终就保存在vma->file中了。这里的vma是由Linux内核的文件系统层传进来的,它的类型为struct vm_area_struct,它表示的是当前进程空间中一块连续的虚拟地址空间,它的起始地址可以由用户来指定,也可以由内核自己来分配,这里我们从JNI方法native_mmap调用的mmap的第一个参数为NULL可以看出,这块连续的虚拟地址空间的起始地址是由内核来指定的。文件内存映射操作完成后,用户访问这个范围的地址空间就相当于是访问对应的文件的内容了。有关Linux文件的内存映射操作,同样可以参考前面Android学习启动篇一文中提到的一本参考书籍《Linux内核源代码情景分析》的第二章内存管理第十三小节系统调用mmap。从这里我们也可以看出,Android系统的匿名共享内存是在虚拟地址空间连续的,但是在物理地址空间就不一定是连续的了。

        同时,这个临时文件vmfile也会保存asma->file域中,这样,Ashmem驱动程序后面就可以通过在asma->file来操作这个匿名内存共享文件了。

        函数ashmem_mmap执行完成后,经过层层返回到JNI方法native_mmap中去,就从mmap函数的返回值中得到了这块虚拟空间的起始地址了,这个起始地址最终返回到应用程序框架层的MemoryFile类的构造函数中,并且保存在成员变量mAddress中,后面,共享内存的读写操作就是对这个地址空间进行操作了。

        三. 匿名共享内存的读写操作

        因为前面对匿名共享内存文件进行内存映射操作,这里对匿名内存文件内容的读写操作就比较简单了,就像访问内存变量一样就行了。

        我们来看一下MemoryFile类的读写操作函数:

 
 
  1. public class MemoryFile  
  2. {  
  3.     ......  
  4.  
  5.     private static native int native_read(FileDescriptor fd, int address, byte[] buffer,  
  6.         int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;  
  7.     private static native void native_write(FileDescriptor fd, int address, byte[] buffer,  
  8.         int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;  
  9.       
  10.     ......  
  11.  
  12.     private FileDescriptor mFD;        // ashmem file descriptor  
  13.     private int mAddress;   // address of ashmem memory  
  14.     private int mLength;    // total length of our ashmem region  
  15.     private boolean mAllowPurging = false;  // true if our ashmem region is unpinned  
  16.  
  17.     ......  
  18.  
  19.     /**  
  20.     * Reads bytes from the memory file.  
  21.     * Will throw an IOException if the file has been purged.  
  22.     *  
  23.     * @param buffer byte array to read bytes into.  
  24.     * @param srcOffset offset into the memory file to read from.  
  25.     * @param destOffset offset into the byte array buffer to read into.  
  26.     * @param count number of bytes to read.  
  27.     * @return number of bytes read.  
  28.     * @throws IOException if the memory file has been purged or deactivated.  
  29.     */  
  30.     public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)   
  31.     throws IOException {  
  32.         if (isDeactivated()) {  
  33.             throw new IOException("Can't read from deactivated memory file.");  
  34.         }  
  35.         if (destOffset < 0 || destOffset > buffer.length || count < 0  
  36.             || count > buffer.length - destOffset  
  37.             || srcOffset < 0 || srcOffset > mLength  
  38.             || count > mLength - srcOffset) {  
  39.                 throw new IndexOutOfBoundsException();  
  40.         }  
  41.         return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);  
  42.     }  
  43.  
  44.     /**  
  45.     * Write bytes to the memory file.  
  46.     * Will throw an IOException if the file has been purged.  
  47.     *  
  48.     * @param buffer byte array to write bytes from.  
  49.     * @param srcOffset offset into the byte array buffer to write from.  
  50.     * @param destOffset offset  into the memory file to write to.  
  51.     * @param count number of bytes to write.  
  52.     * @throws IOException if the memory file has been purged or deactivated.  
  53.     */  
  54.     public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)  
  55.         throws IOException {  
  56.             if (isDeactivated()) {  
  57.                 throw new IOException("Can't write to deactivated memory file.");  
  58.             }  
  59.             if (srcOffset < 0 || srcOffset > buffer.length || count < 0  
  60.                 || count > buffer.length - srcOffset  
  61.                 || destOffset < 0 || destOffset > mLength  
  62.                 || count > mLength - destOffset) {  
  63.                     throw new IndexOutOfBoundsException();  
  64.             }  
  65.             native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);  
  66.     }  
  67.  
  68.     ......  

        这里,我们可以看到,MemoryFile的匿名共享内存读写操作都是通过JNI方法来实现的,读操作和写操作的JNI方法分别是native_read和native_write,它们都是定义在frameworks/base/core/jni/adroid_os_MemoryFile.cpp文件中:

 
 
  1. static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,  
  2.         jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,  
  3.         jint count, jboolean unpinned)  
  4. {  
  5.     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);  
  6.     if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {  
  7.         ashmem_unpin_region(fd, 0, 0);  
  8.         jniThrowException(env, "java/io/IOException""ashmem region was purged");  
  9.         return -1;  
  10.     }  
  11.  
  12.     env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);  
  13.  
  14.     if (unpinned) {  
  15.         ashmem_unpin_region(fd, 0, 0);  
  16.     }  
  17.     return count;  
  18. }  
  19.  
  20. static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,  
  21.         jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,  
  22.         jint count, jboolean unpinned)  
  23. {  
  24.     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);  
  25.     if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {  
  26.         ashmem_unpin_region(fd, 0, 0);  
  27.         jniThrowException(env, "java/io/IOException""ashmem region was purged");  
  28.         return -1;  
  29.     }  
  30.  
  31.     env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);  
  32.  
  33.     if (unpinned) {  
  34.         ashmem_unpin_region(fd, 0, 0);  
  35.     }  
  36.     return count;  

        这里的address参数就是我们在前面执行mmap来映射匿名共享内存文件到内存中时,得到的进程虚拟地址空间的起始地址了,因此,这里就直接可以访问,不必进入到Ashmem驱动程序中去,这也是为什么Ashmem驱动程序没有提供read和write文件操作的原因。

 

        这里我们看到的ashmem_pin_region和ashmem_unpin_region两个函数是系统运行时库提供的接口,用来执行我们前面说的匿名共享内存的锁定和解锁操作,它们的作用是告诉Ashmem驱动程序,它的哪些内存块是正在使用的,需要锁定,哪些内存是不需要使用了,可以它解锁,这样,Ashmem驱动程序就可以辅助内存管理系统来有效地管理内存了。下面我们就看看Ashmem驱动程序是如果辅助内存管理系统来有效地管理内存的。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/965462,如需转载请自行联系原作者

目录
相关文章
|
3天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
3天前
|
算法 JavaScript Android开发
|
5天前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
5天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
6天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
5天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
18 5
|
3天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
5天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
16 3
|
7天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
13天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
42 5